1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2008, Freescale Semiconductor, Inc
6 * Based vaguely on the Linux code
14 #include <linux/math64.h>
15 #include "mmc_private.h"
17 static ulong
mmc_erase_t(struct mmc
*mmc
, ulong start
, lbaint_t blkcnt
)
21 int err
, start_cmd
, end_cmd
;
23 if (mmc
->high_capacity
) {
24 end
= start
+ blkcnt
- 1;
26 end
= (start
+ blkcnt
- 1) * mmc
->write_bl_len
;
27 start
*= mmc
->write_bl_len
;
31 start_cmd
= SD_CMD_ERASE_WR_BLK_START
;
32 end_cmd
= SD_CMD_ERASE_WR_BLK_END
;
34 start_cmd
= MMC_CMD_ERASE_GROUP_START
;
35 end_cmd
= MMC_CMD_ERASE_GROUP_END
;
38 cmd
.cmdidx
= start_cmd
;
40 cmd
.resp_type
= MMC_RSP_R1
;
42 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
49 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
53 cmd
.cmdidx
= MMC_CMD_ERASE
;
54 cmd
.cmdarg
= MMC_ERASE_ARG
;
55 cmd
.resp_type
= MMC_RSP_R1b
;
57 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
64 puts("mmc erase failed\n");
69 ulong
mmc_berase(struct udevice
*dev
, lbaint_t start
, lbaint_t blkcnt
)
71 ulong
mmc_berase(struct blk_desc
*block_dev
, lbaint_t start
, lbaint_t blkcnt
)
75 struct blk_desc
*block_dev
= dev_get_uclass_platdata(dev
);
77 int dev_num
= block_dev
->devnum
;
79 u32 start_rem
, blkcnt_rem
;
80 struct mmc
*mmc
= find_mmc_device(dev_num
);
81 lbaint_t blk
= 0, blk_r
= 0;
87 err
= blk_select_hwpart_devnum(IF_TYPE_MMC
, dev_num
,
93 * We want to see if the requested start or total block count are
94 * unaligned. We discard the whole numbers and only care about the
97 err
= div_u64_rem(start
, mmc
->erase_grp_size
, &start_rem
);
98 err
= div_u64_rem(blkcnt
, mmc
->erase_grp_size
, &blkcnt_rem
);
99 if (start_rem
|| blkcnt_rem
)
100 printf("\n\nCaution! Your devices Erase group is 0x%x\n"
101 "The erase range would be change to "
102 "0x" LBAF
"~0x" LBAF
"\n\n",
103 mmc
->erase_grp_size
, start
& ~(mmc
->erase_grp_size
- 1),
104 ((start
+ blkcnt
+ mmc
->erase_grp_size
)
105 & ~(mmc
->erase_grp_size
- 1)) - 1);
107 while (blk
< blkcnt
) {
108 if (IS_SD(mmc
) && mmc
->ssr
.au
) {
109 blk_r
= ((blkcnt
- blk
) > mmc
->ssr
.au
) ?
110 mmc
->ssr
.au
: (blkcnt
- blk
);
112 blk_r
= ((blkcnt
- blk
) > mmc
->erase_grp_size
) ?
113 mmc
->erase_grp_size
: (blkcnt
- blk
);
115 err
= mmc_erase_t(mmc
, start
+ blk
, blk_r
);
121 /* Waiting for the ready status */
122 if (mmc_send_status(mmc
, timeout
))
129 static ulong
mmc_write_blocks(struct mmc
*mmc
, lbaint_t start
,
130 lbaint_t blkcnt
, const void *src
)
133 struct mmc_data data
;
136 if ((start
+ blkcnt
) > mmc_get_blk_desc(mmc
)->lba
) {
137 printf("MMC: block number 0x" LBAF
" exceeds max(0x" LBAF
")\n",
138 start
+ blkcnt
, mmc_get_blk_desc(mmc
)->lba
);
144 else if (blkcnt
== 1)
145 cmd
.cmdidx
= MMC_CMD_WRITE_SINGLE_BLOCK
;
147 cmd
.cmdidx
= MMC_CMD_WRITE_MULTIPLE_BLOCK
;
149 if (mmc
->high_capacity
)
152 cmd
.cmdarg
= start
* mmc
->write_bl_len
;
154 cmd
.resp_type
= MMC_RSP_R1
;
157 data
.blocks
= blkcnt
;
158 data
.blocksize
= mmc
->write_bl_len
;
159 data
.flags
= MMC_DATA_WRITE
;
161 if (mmc_send_cmd(mmc
, &cmd
, &data
)) {
162 printf("mmc write failed\n");
166 /* SPI multiblock writes terminate using a special
167 * token, not a STOP_TRANSMISSION request.
169 if (!mmc_host_is_spi(mmc
) && blkcnt
> 1) {
170 cmd
.cmdidx
= MMC_CMD_STOP_TRANSMISSION
;
172 cmd
.resp_type
= MMC_RSP_R1b
;
173 if (mmc_send_cmd(mmc
, &cmd
, NULL
)) {
174 printf("mmc fail to send stop cmd\n");
179 /* Waiting for the ready status */
180 if (mmc_send_status(mmc
, timeout
))
187 ulong
mmc_bwrite(struct udevice
*dev
, lbaint_t start
, lbaint_t blkcnt
,
190 ulong
mmc_bwrite(struct blk_desc
*block_dev
, lbaint_t start
, lbaint_t blkcnt
,
195 struct blk_desc
*block_dev
= dev_get_uclass_platdata(dev
);
197 int dev_num
= block_dev
->devnum
;
198 lbaint_t cur
, blocks_todo
= blkcnt
;
201 struct mmc
*mmc
= find_mmc_device(dev_num
);
205 err
= blk_select_hwpart_devnum(IF_TYPE_MMC
, dev_num
, block_dev
->hwpart
);
209 if (mmc_set_blocklen(mmc
, mmc
->write_bl_len
))
213 cur
= (blocks_todo
> mmc
->cfg
->b_max
) ?
214 mmc
->cfg
->b_max
: blocks_todo
;
215 if (mmc_write_blocks(mmc
, start
, cur
, src
) != cur
)
219 src
+= cur
* mmc
->write_bl_len
;
220 } while (blocks_todo
> 0);