1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2008, Freescale Semiconductor, Inc 4 * Andy Fleming 5 * 6 * Based vaguely on the Linux code 7 */ 8 9#include <config.h> 10#include <common.h> 11#include <blk.h> 12#include <dm.h> 13#include <part.h> 14#include <div64.h> 15#include <linux/math64.h> 16#include "mmc_private.h" 17 18static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt, u32 args) 19{ 20 struct mmc_cmd cmd; 21 ulong end; 22 int err, start_cmd, end_cmd; 23 24 if (mmc->high_capacity) { 25 end = start + blkcnt - 1; 26 } else { 27 end = (start + blkcnt - 1) * mmc->write_bl_len; 28 start *= mmc->write_bl_len; 29 } 30 31 if (IS_SD(mmc)) { 32 start_cmd = SD_CMD_ERASE_WR_BLK_START; 33 end_cmd = SD_CMD_ERASE_WR_BLK_END; 34 } else { 35 start_cmd = MMC_CMD_ERASE_GROUP_START; 36 end_cmd = MMC_CMD_ERASE_GROUP_END; 37 } 38 39 cmd.cmdidx = start_cmd; 40 cmd.cmdarg = start; 41 cmd.resp_type = MMC_RSP_R1; 42 43 err = mmc_send_cmd(mmc, &cmd, NULL); 44 if (err) 45 goto err_out; 46 47 cmd.cmdidx = end_cmd; 48 cmd.cmdarg = end; 49 50 err = mmc_send_cmd(mmc, &cmd, NULL); 51 if (err) 52 goto err_out; 53 54 cmd.cmdidx = MMC_CMD_ERASE; 55 cmd.cmdarg = args ? args : MMC_ERASE_ARG; 56 cmd.resp_type = MMC_RSP_R1b; 57 58 err = mmc_send_cmd(mmc, &cmd, NULL); 59 if (err) 60 goto err_out; 61 62 return 0; 63 64err_out: 65 puts("mmc erase failed\n"); 66 return err; 67} 68 69#if CONFIG_IS_ENABLED(BLK) 70ulong mmc_berase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt) 71#else 72ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) 73#endif 74{ 75#if CONFIG_IS_ENABLED(BLK) 76 struct blk_desc *block_dev = dev_get_uclass_plat(dev); 77#endif 78 int dev_num = block_dev->devnum; 79 int err = 0; 80 u32 start_rem, blkcnt_rem, erase_args = 0; 81 struct mmc *mmc = find_mmc_device(dev_num); 82 lbaint_t blk = 0, blk_r = 0; 83 int timeout_ms = 1000; 84 85 if (!mmc) 86 return -1; 87 88 err = blk_select_hwpart_devnum(UCLASS_MMC, dev_num, 89 block_dev->hwpart); 90 if (err < 0) 91 return -1; 92 93 /* 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 96 * remainder. 97 */ 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 if (mmc->can_trim) { 102 /* Trim function applies the erase operation to write 103 * blocks instead of erase groups. 104 */ 105 erase_args = MMC_TRIM_ARG; 106 } else { 107 /* The card ignores all LSB's below the erase group 108 * size, rounding down the addess to a erase group 109 * boundary. 110 */ 111 printf("\n\nCaution! Your devices Erase group is 0x%x\n" 112 "The erase range would be change to " 113 "0x" LBAF "~0x" LBAF "\n\n", 114 mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), 115 ((start + blkcnt + mmc->erase_grp_size - 1) 116 & ~(mmc->erase_grp_size - 1)) - 1); 117 } 118 } 119 120 while (blk < blkcnt) { 121 if (IS_SD(mmc) && mmc->ssr.au) { 122 blk_r = ((blkcnt - blk) > mmc->ssr.au) ? 123 mmc->ssr.au : (blkcnt - blk); 124 } else { 125 blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? 126 mmc->erase_grp_size : (blkcnt - blk); 127 } 128 err = mmc_erase_t(mmc, start + blk, blk_r, erase_args); 129 if (err) 130 break; 131 132 blk += blk_r; 133 134 /* Waiting for the ready status */ 135 if (mmc_poll_for_busy(mmc, timeout_ms)) 136 return 0; 137 } 138 139 return blk; 140} 141 142static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, 143 lbaint_t blkcnt, const void *src) 144{ 145 struct mmc_cmd cmd; 146 struct mmc_data data; 147 int timeout_ms = 1000; 148 149 if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) { 150 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 151 start + blkcnt, mmc_get_blk_desc(mmc)->lba); 152 return 0; 153 } 154 155 if (blkcnt == 0) 156 return 0; 157 else if (blkcnt == 1) 158 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; 159 else 160 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; 161 162 if (mmc->high_capacity) 163 cmd.cmdarg = start; 164 else 165 cmd.cmdarg = start * mmc->write_bl_len; 166 167 cmd.resp_type = MMC_RSP_R1; 168 169 data.src = src; 170 data.blocks = blkcnt; 171 data.blocksize = mmc->write_bl_len; 172 data.flags = MMC_DATA_WRITE; 173 174 if (mmc_send_cmd(mmc, &cmd, &data)) { 175 printf("mmc write failed\n"); 176 return 0; 177 } 178 179 /* SPI multiblock writes terminate using a special 180 * token, not a STOP_TRANSMISSION request. 181 */ 182 if (!mmc_host_is_spi(mmc) && blkcnt > 1) { 183 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 184 cmd.cmdarg = 0; 185 cmd.resp_type = MMC_RSP_R1b; 186 if (mmc_send_cmd(mmc, &cmd, NULL)) { 187 printf("mmc fail to send stop cmd\n"); 188 return 0; 189 } 190 } 191 192 /* Waiting for the ready status */ 193 if (mmc_poll_for_busy(mmc, timeout_ms)) 194 return 0; 195 196 return blkcnt; 197} 198 199#if CONFIG_IS_ENABLED(BLK) 200ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, 201 const void *src) 202#else 203ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, 204 const void *src) 205#endif 206{ 207#if CONFIG_IS_ENABLED(BLK) 208 struct blk_desc *block_dev = dev_get_uclass_plat(dev); 209#endif 210 int dev_num = block_dev->devnum; 211 lbaint_t cur, blocks_todo = blkcnt; 212 int err; 213 214 struct mmc *mmc = find_mmc_device(dev_num); 215 if (!mmc) 216 return 0; 217 218 err = blk_select_hwpart_devnum(UCLASS_MMC, dev_num, block_dev->hwpart); 219 if (err < 0) 220 return 0; 221 222 if (mmc_set_blocklen(mmc, mmc->write_bl_len)) 223 return 0; 224 225 do { 226 cur = (blocks_todo > mmc->cfg->b_max) ? 227 mmc->cfg->b_max : blocks_todo; 228 if (mmc_write_blocks(mmc, start, cur, src) != cur) 229 return 0; 230 blocks_todo -= cur; 231 start += cur; 232 src += cur * mmc->write_bl_len; 233 } while (blocks_todo > 0); 234 235 return blkcnt; 236} 237