uboot/drivers/mmc/mmc_write.c
<<
>>
Prefs
   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)
  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 = 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;
  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(IF_TYPE_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                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);
 107
 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);
 112                } else {
 113                        blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
 114                                mmc->erase_grp_size : (blkcnt - blk);
 115                }
 116                err = mmc_erase_t(mmc, start + blk, blk_r);
 117                if (err)
 118                        break;
 119
 120                blk += blk_r;
 121
 122                /* Waiting for the ready status */
 123                if (mmc_poll_for_busy(mmc, timeout_ms))
 124                        return 0;
 125        }
 126
 127        return blk;
 128}
 129
 130static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
 131                lbaint_t blkcnt, const void *src)
 132{
 133        struct mmc_cmd cmd;
 134        struct mmc_data data;
 135        int timeout_ms = 1000;
 136
 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);
 140                return 0;
 141        }
 142
 143        if (blkcnt == 0)
 144                return 0;
 145        else if (blkcnt == 1)
 146                cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
 147        else
 148                cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
 149
 150        if (mmc->high_capacity)
 151                cmd.cmdarg = start;
 152        else
 153                cmd.cmdarg = start * mmc->write_bl_len;
 154
 155        cmd.resp_type = MMC_RSP_R1;
 156
 157        data.src = src;
 158        data.blocks = blkcnt;
 159        data.blocksize = mmc->write_bl_len;
 160        data.flags = MMC_DATA_WRITE;
 161
 162        if (mmc_send_cmd(mmc, &cmd, &data)) {
 163                printf("mmc write failed\n");
 164                return 0;
 165        }
 166
 167        /* SPI multiblock writes terminate using a special
 168         * token, not a STOP_TRANSMISSION request.
 169         */
 170        if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
 171                cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
 172                cmd.cmdarg = 0;
 173                cmd.resp_type = MMC_RSP_R1b;
 174                if (mmc_send_cmd(mmc, &cmd, NULL)) {
 175                        printf("mmc fail to send stop cmd\n");
 176                        return 0;
 177                }
 178        }
 179
 180        /* Waiting for the ready status */
 181        if (mmc_poll_for_busy(mmc, timeout_ms))
 182                return 0;
 183
 184        return blkcnt;
 185}
 186
 187#if CONFIG_IS_ENABLED(BLK)
 188ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
 189                 const void *src)
 190#else
 191ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
 192                 const void *src)
 193#endif
 194{
 195#if CONFIG_IS_ENABLED(BLK)
 196        struct blk_desc *block_dev = dev_get_uclass_plat(dev);
 197#endif
 198        int dev_num = block_dev->devnum;
 199        lbaint_t cur, blocks_todo = blkcnt;
 200        int err;
 201
 202        struct mmc *mmc = find_mmc_device(dev_num);
 203        if (!mmc)
 204                return 0;
 205
 206        err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
 207        if (err < 0)
 208                return 0;
 209
 210        if (mmc_set_blocklen(mmc, mmc->write_bl_len))
 211                return 0;
 212
 213        do {
 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)
 217                        return 0;
 218                blocks_todo -= cur;
 219                start += cur;
 220                src += cur * mmc->write_bl_len;
 221        } while (blocks_todo > 0);
 222
 223        return blkcnt;
 224}
 225