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 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 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 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 169 if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) { 170 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 171 start + blkcnt, mmc_get_blk_desc(mmc)->lba); 172 return 0; 173 } 174 175 if (blkcnt == 0) 176 return 0; 177 else if (blkcnt == 1) 178 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; 179 else 180 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; 181 182 if (mmc->high_capacity) 183 cmd.cmdarg = start; 184 else 185 cmd.cmdarg = start * mmc->write_bl_len; 186 187 cmd.resp_type = MMC_RSP_R1; 188 189 data.src = src; 190 data.blocks = blkcnt; 191 data.blocksize = mmc->write_bl_len; 192 data.flags = MMC_DATA_WRITE; 193 194 if (mmc_send_cmd(mmc, &cmd, &data)) { 195 printf("mmc write failed\n"); 196 return 0; 197 } 198 199 /* SPI multiblock writes terminate using a special 200 * token, not a STOP_TRANSMISSION request. 201 */ 202 if (!mmc_host_is_spi(mmc) && blkcnt > 1) { 203 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 204 cmd.cmdarg = 0; 205 cmd.resp_type = MMC_RSP_R1b; 206 if (mmc_send_cmd(mmc, &cmd, NULL)) { 207 printf("mmc fail to send stop cmd\n"); 208 return 0; 209 } 210 } 211 212 /* Waiting for the ready status */ 213 if (mmc_send_status(mmc, timeout)) 214 return 0; 215 216 return blkcnt; 217 } 218 219 #ifdef CONFIG_BLK 220 ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, 221 const void *src) 222 #else 223 ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, 224 const void *src) 225 #endif 226 { 227 #ifdef CONFIG_BLK 228 struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 229 #endif 230 int dev_num = block_dev->devnum; 231 lbaint_t cur, blocks_todo = blkcnt; 232 int err; 233 234 struct mmc *mmc = find_mmc_device(dev_num); 235 if (!mmc) 236 return 0; 237 238 err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart); 239 if (err < 0) 240 return 0; 241 242 if (mmc_set_blocklen(mmc, mmc->write_bl_len)) 243 return 0; 244 245 do { 246 cur = (blocks_todo > mmc->cfg->b_max) ? 247 mmc->cfg->b_max : blocks_todo; 248 if (mmc_write_blocks(mmc, start, cur, src) != cur) 249 return 0; 250 blocks_todo -= cur; 251 start += cur; 252 src += cur * mmc->write_bl_len; 253 } while (blocks_todo > 0); 254 255 return blkcnt; 256 } 257