| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* |
| * MultiMediaCard (MMC), eMMC and Secure Digital (SD) write support code. |
| * This code is controller independent. |
| */ |
| |
| #include <stdlib.h> |
| |
| #include "sd_mmc.h" |
| #include "storage.h" |
| |
| static uint32_t storage_write(struct storage_media *media, uint32_t start, |
| uint64_t block_count, const void *src) |
| { |
| struct mmc_command cmd; |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| |
| cmd.resp_type = CARD_RSP_R1; |
| cmd.flags = 0; |
| |
| if (block_count > 1) |
| cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; |
| else |
| cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; |
| |
| if (media->high_capacity) |
| cmd.cmdarg = start; |
| else |
| cmd.cmdarg = start * media->write_bl_len; |
| |
| struct mmc_data data; |
| data.src = src; |
| data.blocks = block_count; |
| data.blocksize = media->write_bl_len; |
| data.flags = DATA_FLAG_WRITE; |
| |
| if (ctrlr->send_cmd(ctrlr, &cmd, &data)) { |
| sd_mmc_error("Write failed\n"); |
| return 0; |
| } |
| |
| /* SPI multiblock writes terminate using a special |
| * token, not a STOP_TRANSMISSION request. |
| */ |
| if ((block_count > 1) && !(ctrlr->caps |
| & DRVR_CAP_AUTO_CMD12)) { |
| cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; |
| cmd.cmdarg = 0; |
| cmd.resp_type = CARD_RSP_R1b; |
| cmd.flags = CMD_FLAG_IGNORE_INHIBIT; |
| if (ctrlr->send_cmd(ctrlr, &cmd, NULL)) { |
| sd_mmc_error("Failed to send stop cmd\n"); |
| return 0; |
| } |
| |
| /* Waiting for the ready status */ |
| sd_mmc_send_status(media, SD_MMC_IO_RETRIES); |
| } |
| |
| return block_count; |
| } |
| |
| uint64_t storage_block_write(struct storage_media *media, uint64_t start, |
| uint64_t count, const void *buffer) |
| { |
| const uint8_t *src = (const uint8_t *)buffer; |
| |
| if (storage_block_setup(media, start, count, 0) == 0) |
| return 0; |
| |
| uint64_t todo = count; |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| do { |
| uint64_t cur = MIN(todo, ctrlr->b_max); |
| if (storage_write(media, start, cur, src) != cur) |
| return 0; |
| todo -= cur; |
| start += cur; |
| src += cur * media->write_bl_len; |
| } while (todo > 0); |
| return count; |
| } |
| |
| uint64_t storage_block_fill_write(struct storage_media *media, uint64_t start, |
| uint64_t count, uint32_t fill_pattern) |
| { |
| if (storage_block_setup(media, start, count, 0) == 0) |
| return 0; |
| |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| uint64_t block_size = media->write_bl_len; |
| /* |
| * We allocate max 4 MiB buffer on heap and set it to fill_pattern and |
| * perform mmc_write operation using this 4MiB buffer until requested |
| * size on disk is written by the fill byte. |
| * |
| * 4MiB was chosen after repeating several experiments with the max |
| * buffer size to be used. Using 1 lba i.e. block_size buffer results in |
| * very large fill_write time. On the other hand, choosing 4MiB, 8MiB or |
| * even 128 Mib resulted in similar write times. With 2MiB, the |
| * fill_write time increased by several seconds. So, 4MiB was chosen as |
| * the default max buffer size. |
| */ |
| uint64_t heap_lba = (4 * MiB) / block_size; |
| /* |
| * Actual allocated buffer size is minimum of three entities: |
| * 1) 4MiB equivalent in lba |
| * 2) count: Number of lbas to overwrite |
| * 3) ctrlr->b_max: Max lbas that the block device allows write |
| * operation on at a time. |
| */ |
| uint64_t buffer_lba = MIN(MIN(heap_lba, count), ctrlr->b_max); |
| |
| uint64_t buffer_bytes = buffer_lba * block_size; |
| uint64_t buffer_words = buffer_bytes / sizeof(uint32_t); |
| uint32_t *buffer = malloc(buffer_bytes); |
| uint32_t *ptr = buffer; |
| |
| for (; buffer_words ; buffer_words--) |
| *ptr++ = fill_pattern; |
| |
| uint64_t todo = count; |
| int ret = 0; |
| |
| do { |
| uint64_t curr_lba = MIN(buffer_lba, todo); |
| |
| if (storage_write(media, start, curr_lba, buffer) != curr_lba) |
| goto cleanup; |
| todo -= curr_lba; |
| start += curr_lba; |
| } while (todo > 0); |
| |
| ret = count; |
| |
| cleanup: |
| free(buffer); |
| return ret; |
| } |