blob: 7950a8c45385da8d1e47a164db83a19e9b627ef1 [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
Patrick Georgiafd4c872020-05-05 23:43:18 +02002/*
Martin Roth0443ac22019-08-30 21:29:41 -06003 * MultiMediaCard (MMC), eMMC and Secure Digital (SD) write support code.
4 * This code is controller independent.
Lee Leahyeef40eb2017-03-23 10:54:57 -07005 */
6
Elyes HAOUASa1e22b82019-03-18 22:49:36 +01007#include <stdlib.h>
8
Lee Leahyeef40eb2017-03-23 10:54:57 -07009#include "sd_mmc.h"
10#include "storage.h"
Lee Leahyeef40eb2017-03-23 10:54:57 -070011
12static 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
63uint64_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
84uint64_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
Elyes HAOUASa342f392018-10-17 10:56:26 +0200119 for (; buffer_words ; buffer_words--)
Lee Leahyeef40eb2017-03-23 10:54:57 -0700120 *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
136cleanup:
137 free(buffer);
138 return ret;
139}