2 * Copyright 2008, Freescale Semiconductor, Inc
5 * Based vaguely on the Linux code
7 * SPDX-License-Identifier: GPL-2.0+
15 #include <linux/math64.h>
16 #include "mmc_private.h"
18 static ulong
mmc_erase_t(struct mmc
*mmc
, ulong start
, lbaint_t blkcnt
)
22 int err
, start_cmd
, end_cmd
;
24 if (mmc
->high_capacity
) {
25 end
= start
+ blkcnt
- 1;
27 end
= (start
+ blkcnt
- 1) * mmc
->write_bl_len
;
28 start
*= mmc
->write_bl_len
;
32 start_cmd
= SD_CMD_ERASE_WR_BLK_START
;
33 end_cmd
= SD_CMD_ERASE_WR_BLK_END
;
35 start_cmd
= MMC_CMD_ERASE_GROUP_START
;
36 end_cmd
= MMC_CMD_ERASE_GROUP_END
;
39 cmd
.cmdidx
= start_cmd
;
41 cmd
.resp_type
= MMC_RSP_R1
;
43 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
50 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
54 cmd
.cmdidx
= MMC_CMD_ERASE
;
55 cmd
.cmdarg
= MMC_ERASE_ARG
;
56 cmd
.resp_type
= MMC_RSP_R1b
;
58 err
= mmc_send_cmd(mmc
, &cmd
, NULL
);
65 puts("mmc erase failed\n");
70 ulong
mmc_berase(struct udevice
*dev
, lbaint_t start
, lbaint_t blkcnt
)
72 ulong
mmc_berase(struct blk_desc
*block_dev
, lbaint_t start
, lbaint_t blkcnt
)
76 struct blk_desc
*block_dev
= dev_get_uclass_platdata(dev
);
78 int dev_num
= block_dev
->devnum
;
80 u32 start_rem
, blkcnt_rem
;
81 struct mmc
*mmc
= find_mmc_device(dev_num
);
82 lbaint_t blk
= 0, blk_r
= 0;
88 err
= blk_select_hwpart_devnum(IF_TYPE_MMC
, dev_num
,
94 * We want to see if the requested start or total block count are
95 * unaligned. We discard the whole numbers and only care about the
98 err
= div_u64_rem(start
, mmc
->erase_grp_size
, &start_rem
);
99 err
= div_u64_rem(blkcnt
, mmc
->erase_grp_size
, &blkcnt_rem
);
100 if (start_rem
|| blkcnt_rem
)
101 printf("\n\nCaution! Your devices Erase group is 0x%x\n"
102 "The erase range would be change to "
103 "0x" LBAF
"~0x" LBAF
"\n\n",
104 mmc
->erase_grp_size
, start
& ~(mmc
->erase_grp_size
- 1),
105 ((start
+ blkcnt
+ mmc
->erase_grp_size
)
106 & ~(mmc
->erase_grp_size
- 1)) - 1);
108 while (blk
< blkcnt
) {
109 if (IS_SD(mmc
) && mmc
->ssr
.au
) {
110 blk_r
= ((blkcnt
- blk
) > mmc
->ssr
.au
) ?
111 mmc
->ssr
.au
: (blkcnt
- blk
);
113 blk_r
= ((blkcnt
- blk
) > mmc
->erase_grp_size
) ?
114 mmc
->erase_grp_size
: (blkcnt
- blk
);
116 err
= mmc_erase_t(mmc
, start
+ blk
, blk_r
);
122 /* Waiting for the ready status */
123 if (mmc_send_status(mmc
, timeout
))
130 static ulong
mmc_write_blocks(struct mmc
*mmc
, lbaint_t start
,
131 lbaint_t blkcnt
, const void *src
)
134 struct mmc_data data
;
137 if ((start
+ blkcnt
) > mmc_get_blk_desc(mmc
)->lba
) {
138 printf("MMC: block number 0x" LBAF
" exceeds max(0x" LBAF
")\n",
139 start
+ blkcnt
, mmc_get_blk_desc(mmc
)->lba
);
145 else if (blkcnt
== 1)
146 cmd
.cmdidx
= MMC_CMD_WRITE_SINGLE_BLOCK
;
148 cmd
.cmdidx
= MMC_CMD_WRITE_MULTIPLE_BLOCK
;
150 if (mmc
->high_capacity
)
153 cmd
.cmdarg
= start
* mmc
->write_bl_len
;
155 cmd
.resp_type
= MMC_RSP_R1
;
158 data
.blocks
= blkcnt
;
159 data
.blocksize
= mmc
->write_bl_len
;
160 data
.flags
= MMC_DATA_WRITE
;
162 if (mmc_send_cmd(mmc
, &cmd
, &data
)) {
163 printf("mmc write failed\n");
167 /* SPI multiblock writes terminate using a special
168 * token, not a STOP_TRANSMISSION request.
170 if (!mmc_host_is_spi(mmc
) && blkcnt
> 1) {
171 cmd
.cmdidx
= MMC_CMD_STOP_TRANSMISSION
;
173 cmd
.resp_type
= MMC_RSP_R1b
;
174 if (mmc_send_cmd(mmc
, &cmd
, NULL
)) {
175 printf("mmc fail to send stop cmd\n");
180 /* Waiting for the ready status */
181 if (mmc_send_status(mmc
, timeout
))
188 ulong
mmc_bwrite(struct udevice
*dev
, lbaint_t start
, lbaint_t blkcnt
,
191 ulong
mmc_bwrite(struct blk_desc
*block_dev
, lbaint_t start
, lbaint_t blkcnt
,
196 struct blk_desc
*block_dev
= dev_get_uclass_platdata(dev
);
198 int dev_num
= block_dev
->devnum
;
199 lbaint_t cur
, blocks_todo
= blkcnt
;
202 struct mmc
*mmc
= find_mmc_device(dev_num
);
206 err
= blk_select_hwpart_devnum(IF_TYPE_MMC
, dev_num
, block_dev
->hwpart
);
210 if (mmc_set_blocklen(mmc
, mmc
->write_bl_len
))
214 cur
= (blocks_todo
> mmc
->cfg
->b_max
) ?
215 mmc
->cfg
->b_max
: blocks_todo
;
216 if (mmc_write_blocks(mmc
, start
, cur
, src
) != cur
)
220 src
+= cur
* mmc
->write_bl_len
;
221 } while (blocks_todo
> 0);