/ src / commonlib / storage / storage_write.c
storage_write.c
  1  /* SPDX-License-Identifier: GPL-2.0-or-later */
  2  /*
  3   * MultiMediaCard (MMC), eMMC and Secure Digital (SD) write support code.
  4   * This code is controller independent.
  5   */
  6  
  7  #include <stdlib.h>
  8  
  9  #include "sd_mmc.h"
 10  #include "storage.h"
 11  
 12  static uint32_t storage_write(struct storage_media *media, uint32_t start,
 13  	uint64_t block_count, const void *src)
 14  {
 15  	struct mmc_command cmd;
 16  	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
 17  
 18  	cmd.resp_type = CARD_RSP_R1;
 19  	cmd.flags = 0;
 20  
 21  	if (block_count > 1)
 22  		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
 23  	else
 24  		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
 25  
 26  	if (media->high_capacity)
 27  		cmd.cmdarg = start;
 28  	else
 29  		cmd.cmdarg = start * media->write_bl_len;
 30  
 31  	struct mmc_data data;
 32  	data.src = src;
 33  	data.blocks = block_count;
 34  	data.blocksize = media->write_bl_len;
 35  	data.flags = DATA_FLAG_WRITE;
 36  
 37  	if (ctrlr->send_cmd(ctrlr, &cmd, &data)) {
 38  		sd_mmc_error("Write failed\n");
 39  		return 0;
 40  	}
 41  
 42  	/* SPI multiblock writes terminate using a special
 43  	 * token, not a STOP_TRANSMISSION request.
 44  	 */
 45  	if ((block_count > 1) && !(ctrlr->caps
 46  		& DRVR_CAP_AUTO_CMD12)) {
 47  		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
 48  		cmd.cmdarg = 0;
 49  		cmd.resp_type = CARD_RSP_R1b;
 50  		cmd.flags = CMD_FLAG_IGNORE_INHIBIT;
 51  		if (ctrlr->send_cmd(ctrlr, &cmd, NULL)) {
 52  			sd_mmc_error("Failed to send stop cmd\n");
 53  			return 0;
 54  		}
 55  
 56  		/* Waiting for the ready status */
 57  		sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
 58  	}
 59  
 60  	return block_count;
 61  }
 62  
 63  uint64_t storage_block_write(struct storage_media *media, uint64_t start,
 64  	uint64_t count, const void *buffer)
 65  {
 66  	const uint8_t *src = (const uint8_t *)buffer;
 67  
 68  	if (storage_block_setup(media, start, count, 0) == 0)
 69  		return 0;
 70  
 71  	uint64_t todo = count;
 72  	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
 73  	do {
 74  		uint64_t cur = MIN(todo, ctrlr->b_max);
 75  		if (storage_write(media, start, cur, src) != cur)
 76  			return 0;
 77  		todo -= cur;
 78  		start += cur;
 79  		src += cur * media->write_bl_len;
 80  	} while (todo > 0);
 81  	return count;
 82  }
 83  
 84  uint64_t storage_block_fill_write(struct storage_media *media, uint64_t start,
 85  	uint64_t count, uint32_t fill_pattern)
 86  {
 87  	if (storage_block_setup(media, start, count, 0) == 0)
 88  		return 0;
 89  
 90  	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
 91  	uint64_t block_size = media->write_bl_len;
 92  	/*
 93  	 * We allocate max 4 MiB buffer on heap and set it to fill_pattern and
 94  	 * perform mmc_write operation using this 4MiB buffer until requested
 95  	 * size on disk is written by the fill byte.
 96  	 *
 97  	 * 4MiB was chosen after repeating several experiments with the max
 98  	 * buffer size to be used. Using 1 lba i.e. block_size buffer results in
 99  	 * very large fill_write time. On the other hand, choosing 4MiB, 8MiB or
100  	 * even 128 Mib resulted in similar write times. With 2MiB, the
101  	 * fill_write time increased by several seconds. So, 4MiB was chosen as
102  	 * the default max buffer size.
103  	 */
104  	uint64_t heap_lba = (4 * MiB) / block_size;
105  	/*
106  	 * Actual allocated buffer size is minimum of three entities:
107  	 * 1) 4MiB equivalent in lba
108  	 * 2) count: Number of lbas to overwrite
109  	 * 3) ctrlr->b_max: Max lbas that the block device allows write
110  	 * operation on at a time.
111  	 */
112  	uint64_t buffer_lba = MIN(MIN(heap_lba, count), ctrlr->b_max);
113  
114  	uint64_t buffer_bytes = buffer_lba * block_size;
115  	uint64_t buffer_words = buffer_bytes / sizeof(uint32_t);
116  	uint32_t *buffer = malloc(buffer_bytes);
117  	uint32_t *ptr = buffer;
118  
119  	for (; buffer_words ; buffer_words--)
120  		*ptr++ = fill_pattern;
121  
122  	uint64_t todo = count;
123  	int ret = 0;
124  
125  	do {
126  		uint64_t curr_lba = MIN(buffer_lba, todo);
127  
128  		if (storage_write(media, start, curr_lba, buffer) != curr_lba)
129  			goto cleanup;
130  		todo -= curr_lba;
131  		start += curr_lba;
132  	} while (todo > 0);
133  
134  	ret = count;
135  
136  cleanup:
137  	free(buffer);
138  	return ret;
139  }