"Das U-Boot" Source Tree
at master 227 lines 5.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * MTD block - abstraction over MTD subsystem, allowing 4 * to read and write in blocks using BLK UCLASS. 5 * 6 * - Read algorithm: 7 * 8 * 1. Convert start block number to start address. 9 * 2. Read block_dev->blksz bytes using mtd_read() and 10 * add to start address pointer block_dev->blksz bytes, 11 * until the requested number of blocks have been read. 12 * 13 * - Write algorithm: 14 * 15 * 1. Convert start block number to start address. 16 * 2. Round this address down by mtd->erasesize. 17 * 18 * Erase addr Start addr 19 * | | 20 * v v 21 * +----------------+----------------+----------------+ 22 * | blksz | blksz | blksz | 23 * +----------------+----------------+----------------+ 24 * 25 * 3. Calculate offset between this two addresses. 26 * 4. Read mtd->erasesize bytes using mtd_read() into 27 * temporary buffer from erase address. 28 * 29 * Erase addr Start addr 30 * | | 31 * v v 32 * +----------------+----------------+----------------+ 33 * | blksz | blksz | blksz | 34 * +----------------+----------------+----------------+ 35 * ^ 36 * | 37 * | 38 * mtd_read() 39 * from here 40 * 41 * 5. Copy data from user buffer to temporary buffer with offset, 42 * calculated at step 3. 43 * 6. Erase and write mtd->erasesize bytes at erase address 44 * pointer using mtd_erase/mtd_write(). 45 * 7. Add to erase address pointer mtd->erasesize bytes. 46 * 8. goto 1 until the requested number of blocks have 47 * been written. 48 * 49 * (C) Copyright 2024 SaluteDevices, Inc. 50 * 51 * Author: Alexey Romanov <avromanov@salutedevices.com> 52 */ 53 54#include <blk.h> 55#include <part.h> 56#include <dm/device.h> 57#include <dm/device-internal.h> 58#include <linux/mtd/mtd.h> 59 60int mtd_bind(struct udevice *dev, struct mtd_info **mtd) 61{ 62 struct blk_desc *bdesc; 63 struct udevice *bdev; 64 int ret; 65 66 ret = blk_create_devicef(dev, "mtd_blk", "blk", UCLASS_MTD, 67 -1, 512, 0, &bdev); 68 if (ret) { 69 pr_err("Cannot create block device\n"); 70 return ret; 71 } 72 73 bdesc = dev_get_uclass_plat(bdev); 74 dev_set_priv(bdev, mtd); 75 bdesc->bdev = bdev; 76 bdesc->part_type = PART_TYPE_MTD; 77 78 return 0; 79} 80 81static ulong mtd_blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, 82 void *dst) 83{ 84 struct blk_desc *block_dev = dev_get_uclass_plat(dev); 85 struct mtd_info *mtd = blk_desc_to_mtd(block_dev); 86 unsigned int sect_size = block_dev->blksz; 87 lbaint_t cur = start; 88 ulong read_cnt = 0; 89 90 while (read_cnt < blkcnt) { 91 int ret; 92 loff_t sect_start = cur * sect_size; 93 size_t retlen; 94 95 ret = mtd_read(mtd, sect_start, sect_size, &retlen, dst); 96 if (ret) 97 return ret; 98 99 if (retlen != sect_size) { 100 pr_err("mtdblock: failed to read block 0x" LBAF "\n", cur); 101 return -EIO; 102 } 103 104 cur++; 105 dst += sect_size; 106 read_cnt++; 107 } 108 109 return read_cnt; 110} 111 112static int mtd_erase_write(struct mtd_info *mtd, uint64_t start, const void *src) 113{ 114 int ret; 115 size_t retlen; 116 struct erase_info erase = { 0 }; 117 118 erase.mtd = mtd; 119 erase.addr = start; 120 erase.len = mtd->erasesize; 121 122 ret = mtd_erase(mtd, &erase); 123 if (ret) 124 return ret; 125 126 ret = mtd_write(mtd, start, mtd->erasesize, &retlen, src); 127 if (ret) 128 return ret; 129 130 if (retlen != mtd->erasesize) { 131 pr_err("mtdblock: failed to read block at 0x%llx\n", start); 132 return -EIO; 133 } 134 135 return 0; 136} 137 138static ulong mtd_blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, 139 const void *src) 140{ 141 struct blk_desc *block_dev = dev_get_uclass_plat(dev); 142 struct mtd_info *mtd = blk_desc_to_mtd(block_dev); 143 unsigned int sect_size = block_dev->blksz; 144 lbaint_t cur = start, blocks_todo = blkcnt; 145 ulong write_cnt = 0; 146 u8 *buf; 147 int ret = 0; 148 149 buf = malloc(mtd->erasesize); 150 if (!buf) 151 return -ENOMEM; 152 153 while (blocks_todo > 0) { 154 loff_t sect_start = cur * sect_size; 155 loff_t erase_start = ALIGN_DOWN(sect_start, mtd->erasesize); 156 u32 offset = sect_start - erase_start; 157 size_t cur_size = min_t(size_t, mtd->erasesize - offset, 158 blocks_todo * sect_size); 159 size_t retlen; 160 lbaint_t written; 161 162 ret = mtd_read(mtd, erase_start, mtd->erasesize, &retlen, buf); 163 if (ret) 164 goto out; 165 166 if (retlen != mtd->erasesize) { 167 pr_err("mtdblock: failed to read block 0x" LBAF "\n", cur); 168 ret = -EIO; 169 goto out; 170 } 171 172 memcpy(buf + offset, src, cur_size); 173 174 ret = mtd_erase_write(mtd, erase_start, buf); 175 if (ret) 176 goto out; 177 178 written = cur_size / sect_size; 179 180 blocks_todo -= written; 181 cur += written; 182 src += cur_size; 183 write_cnt += written; 184 } 185 186out: 187 free(buf); 188 189 if (ret) 190 return ret; 191 192 return write_cnt; 193} 194 195static int mtd_blk_probe(struct udevice *dev) 196{ 197 struct blk_desc *bdesc; 198 struct mtd_info *mtd; 199 int ret; 200 201 ret = device_probe(dev); 202 if (ret) { 203 pr_err("Probing %s failed (err=%d)\n", dev->name, ret); 204 return ret; 205 } 206 207 bdesc = dev_get_uclass_plat(dev); 208 mtd = blk_desc_to_mtd(bdesc); 209 210 if (mtd_type_is_nand(mtd)) 211 pr_warn("MTD device '%s' is NAND, please use UBI devices instead\n", 212 mtd->name); 213 214 return 0; 215} 216 217static const struct blk_ops mtd_blk_ops = { 218 .read = mtd_blk_read, 219 .write = mtd_blk_write, 220}; 221 222U_BOOT_DRIVER(mtd_blk) = { 223 .name = "mtd_blk", 224 .id = UCLASS_BLK, 225 .ops = &mtd_blk_ops, 226 .probe = mtd_blk_probe, 227};