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 <part.h> 13 #include "mmc_private.h" 14 15 static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) 16 { 17 struct mmc_cmd cmd; 18 ulong end; 19 int err, start_cmd, end_cmd; 20 21 if (mmc->high_capacity) { 22 end = start + blkcnt - 1; 23 } else { 24 end = (start + blkcnt - 1) * mmc->write_bl_len; 25 start *= mmc->write_bl_len; 26 } 27 28 if (IS_SD(mmc)) { 29 start_cmd = SD_CMD_ERASE_WR_BLK_START; 30 end_cmd = SD_CMD_ERASE_WR_BLK_END; 31 } else { 32 start_cmd = MMC_CMD_ERASE_GROUP_START; 33 end_cmd = MMC_CMD_ERASE_GROUP_END; 34 } 35 36 cmd.cmdidx = start_cmd; 37 cmd.cmdarg = start; 38 cmd.resp_type = MMC_RSP_R1; 39 40 err = mmc_send_cmd(mmc, &cmd, NULL); 41 if (err) 42 goto err_out; 43 44 cmd.cmdidx = end_cmd; 45 cmd.cmdarg = end; 46 47 err = mmc_send_cmd(mmc, &cmd, NULL); 48 if (err) 49 goto err_out; 50 51 cmd.cmdidx = MMC_CMD_ERASE; 52 cmd.cmdarg = SECURE_ERASE; 53 cmd.resp_type = MMC_RSP_R1b; 54 55 err = mmc_send_cmd(mmc, &cmd, NULL); 56 if (err) 57 goto err_out; 58 59 return 0; 60 61 err_out: 62 puts("mmc erase failed\n"); 63 return err; 64 } 65 66 unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) 67 { 68 int err = 0; 69 struct mmc *mmc = find_mmc_device(dev_num); 70 lbaint_t blk = 0, blk_r = 0; 71 int timeout = 1000; 72 73 if (!mmc) 74 return -1; 75 76 if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) 77 printf("\n\nCaution! Your devices Erase group is 0x%x\n" 78 "The erase range would be change to " 79 "0x" LBAF "~0x" LBAF "\n\n", 80 mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), 81 ((start + blkcnt + mmc->erase_grp_size) 82 & ~(mmc->erase_grp_size - 1)) - 1); 83 84 while (blk < blkcnt) { 85 blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? 86 mmc->erase_grp_size : (blkcnt - blk); 87 err = mmc_erase_t(mmc, start + blk, blk_r); 88 if (err) 89 break; 90 91 blk += blk_r; 92 93 /* Waiting for the ready status */ 94 if (mmc_send_status(mmc, timeout)) 95 return 0; 96 } 97 98 return blk; 99 } 100 101 static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, 102 lbaint_t blkcnt, const void *src) 103 { 104 struct mmc_cmd cmd; 105 struct mmc_data data; 106 int timeout = 1000; 107 108 if ((start + blkcnt) > mmc->block_dev.lba) { 109 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 110 start + blkcnt, mmc->block_dev.lba); 111 return 0; 112 } 113 114 if (blkcnt == 0) 115 return 0; 116 else if (blkcnt == 1) 117 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; 118 else 119 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; 120 121 if (mmc->high_capacity) 122 cmd.cmdarg = start; 123 else 124 cmd.cmdarg = start * mmc->write_bl_len; 125 126 cmd.resp_type = MMC_RSP_R1; 127 128 data.src = src; 129 data.blocks = blkcnt; 130 data.blocksize = mmc->write_bl_len; 131 data.flags = MMC_DATA_WRITE; 132 133 if (mmc_send_cmd(mmc, &cmd, &data)) { 134 printf("mmc write failed\n"); 135 return 0; 136 } 137 138 /* SPI multiblock writes terminate using a special 139 * token, not a STOP_TRANSMISSION request. 140 */ 141 if (!mmc_host_is_spi(mmc) && blkcnt > 1) { 142 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 143 cmd.cmdarg = 0; 144 cmd.resp_type = MMC_RSP_R1b; 145 if (mmc_send_cmd(mmc, &cmd, NULL)) { 146 printf("mmc fail to send stop cmd\n"); 147 return 0; 148 } 149 } 150 151 /* Waiting for the ready status */ 152 if (mmc_send_status(mmc, timeout)) 153 return 0; 154 155 return blkcnt; 156 } 157 158 ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) 159 { 160 lbaint_t cur, blocks_todo = blkcnt; 161 162 struct mmc *mmc = find_mmc_device(dev_num); 163 if (!mmc) 164 return 0; 165 166 if (mmc_set_blocklen(mmc, mmc->write_bl_len)) 167 return 0; 168 169 do { 170 cur = (blocks_todo > mmc->cfg->b_max) ? 171 mmc->cfg->b_max : blocks_todo; 172 if (mmc_write_blocks(mmc, start, cur, src) != cur) 173 return 0; 174 blocks_todo -= cur; 175 start += cur; 176 src += cur * mmc->write_bl_len; 177 } while (blocks_todo > 0); 178 179 return blkcnt; 180 } 181