mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-18 02:44:37 +00:00
Merge tag 'u-boot-nand-20240808' of https://source.denx.de/u-boot/custodians/u-boot-nand-flash
This series adds support for the UBI block device, which allows to read/write data block by block. The series was tested by Alexey Romanov on SPI NAND. The patches pass the pipeline CI: https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/pipelines/21933
This commit is contained in:
commit
eb8e25c000
16 changed files with 709 additions and 17 deletions
78
cmd/ubi.c
78
cmd/ubi.c
|
@ -423,18 +423,84 @@ int ubi_volume_begin_write(char *volume, void *buf, size_t size,
|
|||
return ubi_volume_continue_write(volume, buf, size);
|
||||
}
|
||||
|
||||
int ubi_volume_write(char *volume, void *buf, size_t size)
|
||||
static int ubi_volume_offset_write(char *volume, void *buf, loff_t offset,
|
||||
size_t size)
|
||||
{
|
||||
return ubi_volume_begin_write(volume, buf, size, size);
|
||||
int len, tbuf_size, ret;
|
||||
u64 lnum;
|
||||
struct ubi_volume *vol;
|
||||
loff_t off = offset;
|
||||
void *tbuf;
|
||||
|
||||
vol = ubi_find_volume(volume);
|
||||
if (!vol)
|
||||
return -ENODEV;
|
||||
|
||||
if (size > vol->reserved_pebs * (ubi->leb_size - vol->data_pad))
|
||||
return -EINVAL;
|
||||
|
||||
tbuf_size = vol->usable_leb_size;
|
||||
tbuf = malloc_cache_aligned(tbuf_size);
|
||||
if (!tbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
lnum = off;
|
||||
off = do_div(lnum, vol->usable_leb_size);
|
||||
|
||||
do {
|
||||
struct ubi_volume_desc desc = {
|
||||
.vol = vol,
|
||||
.mode = UBI_READWRITE,
|
||||
};
|
||||
|
||||
len = size > tbuf_size ? tbuf_size : size;
|
||||
if (off + len >= vol->usable_leb_size)
|
||||
len = vol->usable_leb_size - off;
|
||||
|
||||
ret = ubi_read(&desc, (int)lnum, tbuf, 0, tbuf_size);
|
||||
if (ret) {
|
||||
pr_err("Failed to read leb %lld (%d)\n", lnum, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(tbuf + off, buf, len);
|
||||
|
||||
ret = ubi_leb_change(&desc, (int)lnum, tbuf, tbuf_size);
|
||||
if (ret) {
|
||||
pr_err("Failed to write leb %lld (%d)\n", lnum, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
off += len;
|
||||
if (off >= vol->usable_leb_size) {
|
||||
lnum++;
|
||||
off -= vol->usable_leb_size;
|
||||
}
|
||||
|
||||
buf += len;
|
||||
size -= len;
|
||||
} while (size);
|
||||
|
||||
exit:
|
||||
free(tbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ubi_volume_read(char *volume, char *buf, size_t size)
|
||||
int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size)
|
||||
{
|
||||
if (!offset)
|
||||
return ubi_volume_begin_write(volume, buf, size, size);
|
||||
|
||||
return ubi_volume_offset_write(volume, buf, offset, size);
|
||||
}
|
||||
|
||||
int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size)
|
||||
{
|
||||
int err, lnum, off, len, tbuf_size;
|
||||
void *tbuf;
|
||||
unsigned long long tmp;
|
||||
struct ubi_volume *vol;
|
||||
loff_t offp = 0;
|
||||
loff_t offp = offset;
|
||||
size_t len_read;
|
||||
|
||||
vol = ubi_find_volume(volume);
|
||||
|
@ -769,7 +835,7 @@ static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|||
(void *)addr, size, full_size);
|
||||
}
|
||||
} else {
|
||||
ret = ubi_volume_write(argv[3], (void *)addr, size);
|
||||
ret = ubi_volume_write(argv[3], (void *)addr, 0, size);
|
||||
}
|
||||
if (!ret) {
|
||||
printf("%lld bytes written to volume %s\n", size,
|
||||
|
@ -795,7 +861,7 @@ static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|||
}
|
||||
|
||||
if (argc == 3) {
|
||||
return ubi_volume_read(argv[3], (char *)addr, size);
|
||||
return ubi_volume_read(argv[3], (char *)addr, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
disk/part.c
10
disk/part.c
|
@ -285,6 +285,13 @@ void part_init(struct blk_desc *desc)
|
|||
|
||||
blkcache_invalidate(desc->uclass_id, desc->devnum);
|
||||
|
||||
if (desc->part_type != PART_TYPE_UNKNOWN) {
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
if (entry->part_type == desc->part_type && !entry->test(desc))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
desc->part_type = PART_TYPE_UNKNOWN;
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
int ret;
|
||||
|
@ -304,7 +311,8 @@ static void print_part_header(const char *type, struct blk_desc *desc)
|
|||
CONFIG_IS_ENABLED(DOS_PARTITION) || \
|
||||
CONFIG_IS_ENABLED(ISO_PARTITION) || \
|
||||
CONFIG_IS_ENABLED(AMIGA_PARTITION) || \
|
||||
CONFIG_IS_ENABLED(EFI_PARTITION)
|
||||
CONFIG_IS_ENABLED(EFI_PARTITION) || \
|
||||
CONFIG_IS_ENABLED(MTD_PARTITIONS)
|
||||
printf("\nPartition Map for %s device %d -- Partition Type: %s\n\n",
|
||||
uclass_get_name(desc->uclass_id), desc->devnum, type);
|
||||
#endif /* any CONFIG_..._PARTITION */
|
||||
|
|
|
@ -36,6 +36,8 @@ static struct {
|
|||
{ UCLASS_PVBLOCK, "pvblock" },
|
||||
{ UCLASS_BLKMAP, "blkmap" },
|
||||
{ UCLASS_RKMTD, "rkmtd" },
|
||||
{ UCLASS_MTD, "mtd" },
|
||||
{ UCLASS_MTD, "ubi" },
|
||||
};
|
||||
|
||||
static enum uclass_id uclass_name_to_iftype(const char *uclass_idname)
|
||||
|
|
|
@ -2,6 +2,7 @@ menu "MTD Support"
|
|||
|
||||
config MTD_PARTITIONS
|
||||
bool
|
||||
select PARTITIONS
|
||||
|
||||
config MTD
|
||||
bool "Enable MTD layer"
|
||||
|
@ -31,6 +32,13 @@ config MTD_CONCAT
|
|||
into a single logical device. The larger logical device can then
|
||||
be partitioned.
|
||||
|
||||
config MTD_BLOCK
|
||||
bool "Enable block device access to MTD devices"
|
||||
depends on BLK
|
||||
help
|
||||
Enable support for block device access to MTD devices
|
||||
using blk_ops abstraction.
|
||||
|
||||
config SYS_MTDPARTS_RUNTIME
|
||||
bool "Allow MTDPARTS to be configured at runtime"
|
||||
help
|
||||
|
|
|
@ -26,6 +26,7 @@ obj-y += onenand/
|
|||
obj-y += spi/
|
||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||
obj-$(CONFIG_NVMXIP) += nvmxip/
|
||||
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
|
||||
|
||||
#SPL/TPL build
|
||||
else
|
||||
|
|
227
drivers/mtd/mtdblock.c
Normal file
227
drivers/mtd/mtdblock.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* MTD block - abstraction over MTD subsystem, allowing
|
||||
* to read and write in blocks using BLK UCLASS.
|
||||
*
|
||||
* - Read algorithm:
|
||||
*
|
||||
* 1. Convert start block number to start address.
|
||||
* 2. Read block_dev->blksz bytes using mtd_read() and
|
||||
* add to start address pointer block_dev->blksz bytes,
|
||||
* until the requested number of blocks have been read.
|
||||
*
|
||||
* - Write algorithm:
|
||||
*
|
||||
* 1. Convert start block number to start address.
|
||||
* 2. Round this address down by mtd->erasesize.
|
||||
*
|
||||
* Erase addr Start addr
|
||||
* | |
|
||||
* v v
|
||||
* +----------------+----------------+----------------+
|
||||
* | blksz | blksz | blksz |
|
||||
* +----------------+----------------+----------------+
|
||||
*
|
||||
* 3. Calculate offset between this two addresses.
|
||||
* 4. Read mtd->erasesize bytes using mtd_read() into
|
||||
* temporary buffer from erase address.
|
||||
*
|
||||
* Erase addr Start addr
|
||||
* | |
|
||||
* v v
|
||||
* +----------------+----------------+----------------+
|
||||
* | blksz | blksz | blksz |
|
||||
* +----------------+----------------+----------------+
|
||||
* ^
|
||||
* |
|
||||
* |
|
||||
* mtd_read()
|
||||
* from here
|
||||
*
|
||||
* 5. Copy data from user buffer to temporary buffer with offset,
|
||||
* calculated at step 3.
|
||||
* 6. Erase and write mtd->erasesize bytes at erase address
|
||||
* pointer using mtd_erase/mtd_write().
|
||||
* 7. Add to erase address pointer mtd->erasesize bytes.
|
||||
* 8. goto 1 until the requested number of blocks have
|
||||
* been written.
|
||||
*
|
||||
* (C) Copyright 2024 SaluteDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <blk.h>
|
||||
#include <part.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
int mtd_bind(struct udevice *dev, struct mtd_info **mtd)
|
||||
{
|
||||
struct blk_desc *bdesc;
|
||||
struct udevice *bdev;
|
||||
int ret;
|
||||
|
||||
ret = blk_create_devicef(dev, "mtd_blk", "blk", UCLASS_MTD,
|
||||
-1, 512, 0, &bdev);
|
||||
if (ret) {
|
||||
pr_err("Cannot create block device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdesc = dev_get_uclass_plat(bdev);
|
||||
dev_set_priv(bdev, mtd);
|
||||
bdesc->bdev = bdev;
|
||||
bdesc->part_type = PART_TYPE_MTD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong mtd_blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
|
||||
void *dst)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
|
||||
struct mtd_info *mtd = blk_desc_to_mtd(block_dev);
|
||||
unsigned int sect_size = block_dev->blksz;
|
||||
lbaint_t cur = start;
|
||||
ulong read_cnt = 0;
|
||||
|
||||
while (read_cnt < blkcnt) {
|
||||
int ret;
|
||||
loff_t sect_start = cur * sect_size;
|
||||
size_t retlen;
|
||||
|
||||
ret = mtd_read(mtd, sect_start, sect_size, &retlen, dst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (retlen != sect_size) {
|
||||
pr_err("mtdblock: failed to read block 0x" LBAF "\n", cur);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cur++;
|
||||
dst += sect_size;
|
||||
read_cnt++;
|
||||
}
|
||||
|
||||
return read_cnt;
|
||||
}
|
||||
|
||||
static int mtd_erase_write(struct mtd_info *mtd, uint64_t start, const void *src)
|
||||
{
|
||||
int ret;
|
||||
size_t retlen;
|
||||
struct erase_info erase = { 0 };
|
||||
|
||||
erase.mtd = mtd;
|
||||
erase.addr = start;
|
||||
erase.len = mtd->erasesize;
|
||||
|
||||
ret = mtd_erase(mtd, &erase);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_write(mtd, start, mtd->erasesize, &retlen, src);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (retlen != mtd->erasesize) {
|
||||
pr_err("mtdblock: failed to read block at 0x%llx\n", start);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong mtd_blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
|
||||
const void *src)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
|
||||
struct mtd_info *mtd = blk_desc_to_mtd(block_dev);
|
||||
unsigned int sect_size = block_dev->blksz;
|
||||
lbaint_t cur = start, blocks_todo = blkcnt;
|
||||
ulong write_cnt = 0;
|
||||
u8 *buf;
|
||||
int ret = 0;
|
||||
|
||||
buf = malloc(mtd->erasesize);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
while (blocks_todo > 0) {
|
||||
loff_t sect_start = cur * sect_size;
|
||||
loff_t erase_start = ALIGN_DOWN(sect_start, mtd->erasesize);
|
||||
u32 offset = sect_start - erase_start;
|
||||
size_t cur_size = min_t(size_t, mtd->erasesize - offset,
|
||||
blocks_todo * sect_size);
|
||||
size_t retlen;
|
||||
lbaint_t written;
|
||||
|
||||
ret = mtd_read(mtd, erase_start, mtd->erasesize, &retlen, buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (retlen != mtd->erasesize) {
|
||||
pr_err("mtdblock: failed to read block 0x" LBAF "\n", cur);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(buf + offset, src, cur_size);
|
||||
|
||||
ret = mtd_erase_write(mtd, erase_start, buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
written = cur_size / sect_size;
|
||||
|
||||
blocks_todo -= written;
|
||||
cur += written;
|
||||
src += cur_size;
|
||||
write_cnt += written;
|
||||
}
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return write_cnt;
|
||||
}
|
||||
|
||||
static int mtd_blk_probe(struct udevice *dev)
|
||||
{
|
||||
struct blk_desc *bdesc;
|
||||
struct mtd_info *mtd;
|
||||
int ret;
|
||||
|
||||
ret = device_probe(dev);
|
||||
if (ret) {
|
||||
pr_err("Probing %s failed (err=%d)\n", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdesc = dev_get_uclass_plat(dev);
|
||||
mtd = blk_desc_to_mtd(bdesc);
|
||||
|
||||
if (mtd_type_is_nand(mtd))
|
||||
pr_warn("MTD device '%s' is NAND, please use UBI devices instead\n",
|
||||
mtd->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct blk_ops mtd_blk_ops = {
|
||||
.read = mtd_blk_read,
|
||||
.write = mtd_blk_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mtd_blk) = {
|
||||
.name = "mtd_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &mtd_blk_ops,
|
||||
.probe = mtd_blk_probe,
|
||||
};
|
|
@ -20,6 +20,8 @@
|
|||
#endif
|
||||
|
||||
#include <malloc.h>
|
||||
#include <memalign.h>
|
||||
#include <part.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/compat.h>
|
||||
|
@ -1054,3 +1056,77 @@ uint64_t mtd_get_device_size(const struct mtd_info *mtd)
|
|||
return mtd->size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_device_size);
|
||||
|
||||
static struct mtd_info *mtd_get_partition_by_index(struct mtd_info *mtd, int index)
|
||||
{
|
||||
struct mtd_info *part;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(part, &mtd->partitions, node)
|
||||
if (i++ == index)
|
||||
return part;
|
||||
|
||||
debug("Partition with idx=%d not found on MTD device %s\n", index, mtd->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __maybe_unused part_get_info_mtd(struct blk_desc *dev_desc, int part_idx,
|
||||
struct disk_partition *info)
|
||||
{
|
||||
struct mtd_info *master = blk_desc_to_mtd(dev_desc);
|
||||
struct mtd_info *part;
|
||||
|
||||
if (!master) {
|
||||
debug("MTD device is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
part = mtd_get_partition_by_index(master, part_idx);
|
||||
if (!part) {
|
||||
debug("Failed to find partition with idx=%d\n", part_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(info->name, PART_NAME_LEN, part->name);
|
||||
info->start = part->offset / dev_desc->blksz;
|
||||
info->size = part->size / dev_desc->blksz;
|
||||
info->blksz = dev_desc->blksz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __maybe_unused part_print_mtd(struct blk_desc *dev_desc)
|
||||
{
|
||||
struct mtd_info *master = blk_desc_to_mtd(dev_desc);
|
||||
struct mtd_info *part;
|
||||
|
||||
if (!master)
|
||||
return;
|
||||
|
||||
list_for_each_entry(part, &master->partitions, node)
|
||||
printf("- 0x%012llx-0x%012llx : \"%s\"\n",
|
||||
part->offset, part->offset + part->size, part->name);
|
||||
}
|
||||
|
||||
static int part_test_mtd(struct blk_desc *dev_desc)
|
||||
{
|
||||
struct mtd_info *master = blk_desc_to_mtd(dev_desc);
|
||||
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
|
||||
|
||||
if (!master)
|
||||
return -1;
|
||||
|
||||
if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_PART_TYPE(mtd) = {
|
||||
.name = "MTD",
|
||||
.part_type = PART_TYPE_MTD,
|
||||
.max_entries = MTD_ENTRY_NUMBERS,
|
||||
.get_info = part_get_info_ptr(part_get_info_mtd),
|
||||
.print = part_print_ptr(part_print_mtd),
|
||||
.test = part_test_mtd,
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <watchdog.h>
|
||||
#include <spi.h>
|
||||
#include <spi-mem.h>
|
||||
#include <ubi_uboot.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/devres.h>
|
||||
#include <linux/bitops.h>
|
||||
|
@ -33,6 +34,10 @@
|
|||
#include <linux/printk.h>
|
||||
#endif
|
||||
|
||||
struct spinand_plat {
|
||||
struct mtd_info *mtd;
|
||||
};
|
||||
|
||||
/* SPI NAND index visible in MTD names */
|
||||
static int spi_nand_idx;
|
||||
|
||||
|
@ -1172,12 +1177,32 @@ static void spinand_cleanup(struct spinand_device *spinand)
|
|||
kfree(spinand->scratchbuf);
|
||||
}
|
||||
|
||||
static int spinand_bind(struct udevice *dev)
|
||||
{
|
||||
if (blk_enabled()) {
|
||||
struct spinand_plat *plat = dev_get_plat(dev);
|
||||
int ret;
|
||||
|
||||
if (CONFIG_IS_ENABLED(MTD_BLOCK)) {
|
||||
ret = mtd_bind(dev, &plat->mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (CONFIG_IS_ENABLED(UBI_BLOCK))
|
||||
return ubi_bind(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_probe(struct udevice *dev)
|
||||
{
|
||||
struct spinand_device *spinand = dev_get_priv(dev);
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
struct mtd_info *mtd = dev_get_uclass_priv(dev);
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct spinand_plat *plat = dev_get_plat(dev);
|
||||
int ret;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
|
@ -1217,6 +1242,8 @@ static int spinand_probe(struct udevice *dev)
|
|||
if (ret)
|
||||
goto err_spinand_cleanup;
|
||||
|
||||
plat->mtd = mtd;
|
||||
|
||||
return 0;
|
||||
|
||||
err_spinand_cleanup:
|
||||
|
@ -1286,4 +1313,6 @@ U_BOOT_DRIVER(spinand) = {
|
|||
.of_match = spinand_ids,
|
||||
.priv_auto = sizeof(struct spinand_device),
|
||||
.probe = spinand_probe,
|
||||
.bind = spinand_bind,
|
||||
.plat_auto = sizeof(struct spinand_plat),
|
||||
};
|
||||
|
|
|
@ -114,5 +114,11 @@ config MTD_UBI_FM_DEBUG
|
|||
help
|
||||
Enable UBI fastmap debug
|
||||
|
||||
config UBI_BLOCK
|
||||
bool "Enable UBI block device support"
|
||||
depends on BLK
|
||||
help
|
||||
Enable UBI block device support using blk_ops abstraction.
|
||||
|
||||
endif # MTD_UBI
|
||||
endmenu # "Enable UBI - Unsorted block images"
|
||||
|
|
|
@ -7,3 +7,4 @@ obj-y += attach.o build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o crc32.o
|
|||
obj-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o
|
||||
obj-y += misc.o
|
||||
obj-y += debug.o
|
||||
obj-$(CONFIG_UBI_BLOCK) += block.o part.o
|
||||
|
|
130
drivers/mtd/ubi/block.c
Normal file
130
drivers/mtd/ubi/block.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2024 SaluteDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <blk.h>
|
||||
#include <part.h>
|
||||
#include <ubi_uboot.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
int ubi_bind(struct udevice *dev)
|
||||
{
|
||||
struct blk_desc *bdesc;
|
||||
struct udevice *bdev;
|
||||
int ret;
|
||||
|
||||
ret = blk_create_devicef(dev, "ubi_blk", "blk", UCLASS_MTD,
|
||||
-1, 512, 0, &bdev);
|
||||
if (ret) {
|
||||
pr_err("Cannot create block device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdesc = dev_get_uclass_plat(bdev);
|
||||
|
||||
bdesc->bdev = bdev;
|
||||
bdesc->part_type = PART_TYPE_UBI;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ubi_device *get_ubi_device(void)
|
||||
{
|
||||
return ubi_devices[0];
|
||||
}
|
||||
|
||||
static char *get_volume_name(int vol_id)
|
||||
{
|
||||
struct ubi_device *ubi = get_ubi_device();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
|
||||
struct ubi_volume *volume = ubi->volumes[i];
|
||||
|
||||
if (!volume)
|
||||
continue;
|
||||
|
||||
if (volume->vol_id >= UBI_INTERNAL_VOL_START)
|
||||
continue;
|
||||
|
||||
if (volume->vol_id == vol_id)
|
||||
return volume->name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ulong ubi_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
|
||||
void *dst)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
|
||||
char *volume_name = get_volume_name(block_dev->hwpart);
|
||||
unsigned int size = blkcnt * block_dev->blksz;
|
||||
loff_t offset = start * block_dev->blksz;
|
||||
int ret;
|
||||
|
||||
if (!volume_name) {
|
||||
pr_err("%s: failed to find volume name for blk=" LBAF "\n", __func__, start);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ubi_volume_read(volume_name, dst, offset, size);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to read from %s UBI volume\n", __func__, volume_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static ulong ubi_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
|
||||
const void *src)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
|
||||
char *volume_name = get_volume_name(block_dev->hwpart);
|
||||
unsigned int size = blkcnt * block_dev->blksz;
|
||||
loff_t offset = start * block_dev->blksz;
|
||||
int ret;
|
||||
|
||||
if (!volume_name) {
|
||||
pr_err("%s: failed to find volume for blk=" LBAF "\n", __func__, start);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ubi_volume_write(volume_name, (void *)src, offset, size);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to write from %s UBI volume\n", __func__, volume_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static int ubi_blk_probe(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = device_probe(dev);
|
||||
if (ret) {
|
||||
pr_err("Probing %s failed (err=%d)\n", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct blk_ops ubi_blk_ops = {
|
||||
.read = ubi_bread,
|
||||
.write = ubi_bwrite,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ubi_blk) = {
|
||||
.name = "ubi_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &ubi_blk_ops,
|
||||
.probe = ubi_blk_probe,
|
||||
};
|
99
drivers/mtd/ubi/part.c
Normal file
99
drivers/mtd/ubi/part.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2024 SaluteDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <memalign.h>
|
||||
#include <part.h>
|
||||
#include <ubi_uboot.h>
|
||||
|
||||
static inline struct ubi_device *get_ubi_device(void)
|
||||
{
|
||||
return ubi_devices[0];
|
||||
}
|
||||
|
||||
static struct ubi_volume *ubi_get_volume_by_index(int vol_id)
|
||||
{
|
||||
struct ubi_device *ubi = get_ubi_device();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
|
||||
struct ubi_volume *volume = ubi->volumes[i];
|
||||
|
||||
if (!volume)
|
||||
continue;
|
||||
|
||||
if (volume->vol_id >= UBI_INTERNAL_VOL_START)
|
||||
continue;
|
||||
|
||||
if (volume->vol_id == vol_id)
|
||||
return volume;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __maybe_unused part_get_info_ubi(struct blk_desc *dev_desc, int part_idx,
|
||||
struct disk_partition *info)
|
||||
{
|
||||
struct ubi_volume *vol;
|
||||
|
||||
/*
|
||||
* We must use part_idx - 1 instead of part_idx, because
|
||||
* part_get_info_by_name() start indexing at 1, not 0.
|
||||
* ubi volumes idexed starting at 0
|
||||
*/
|
||||
vol = ubi_get_volume_by_index(part_idx - 1);
|
||||
if (!vol)
|
||||
return 0;
|
||||
|
||||
snprintf(info->name, PART_NAME_LEN, vol->name);
|
||||
|
||||
info->start = 0;
|
||||
info->size = (unsigned long)vol->used_bytes / dev_desc->blksz;
|
||||
info->blksz = dev_desc->blksz;
|
||||
|
||||
/* Save UBI volume ID in blk device descriptor */
|
||||
dev_desc->hwpart = vol->vol_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __maybe_unused part_print_ubi(struct blk_desc *dev_desc)
|
||||
{
|
||||
struct ubi_device *ubi = get_ubi_device();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
|
||||
struct ubi_volume *volume = ubi->volumes[i];
|
||||
|
||||
if (!volume)
|
||||
continue;
|
||||
|
||||
if (volume->vol_id >= UBI_INTERNAL_VOL_START)
|
||||
continue;
|
||||
|
||||
printf("%d: %s\n", volume->vol_id, volume->name);
|
||||
}
|
||||
}
|
||||
|
||||
static int part_test_ubi(struct blk_desc *dev_desc)
|
||||
{
|
||||
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
|
||||
|
||||
if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_PART_TYPE(ubi) = {
|
||||
.name = "ubi",
|
||||
.part_type = PART_TYPE_UBI,
|
||||
.max_entries = UBI_ENTRY_NUMBERS,
|
||||
.get_info = part_get_info_ptr(part_get_info_ubi),
|
||||
.print = part_print_ptr(part_print_ubi),
|
||||
.test = part_test_ubi,
|
||||
};
|
16
env/ubi.c
vendored
16
env/ubi.c
vendored
|
@ -53,7 +53,7 @@ static int env_ubi_save(void)
|
|||
if (gd->env_valid == ENV_VALID) {
|
||||
puts("Writing to redundant UBI... ");
|
||||
if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME_REDUND,
|
||||
(void *)env_new, CONFIG_ENV_SIZE)) {
|
||||
(void *)env_new, 0, CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to write env to %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART,
|
||||
CONFIG_ENV_UBI_VOLUME_REDUND);
|
||||
|
@ -62,7 +62,7 @@ static int env_ubi_save(void)
|
|||
} else {
|
||||
puts("Writing to UBI... ");
|
||||
if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME,
|
||||
(void *)env_new, CONFIG_ENV_SIZE)) {
|
||||
(void *)env_new, 0, CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to write env to %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART,
|
||||
CONFIG_ENV_UBI_VOLUME);
|
||||
|
@ -92,7 +92,7 @@ static int env_ubi_save(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME, (void *)env_new,
|
||||
if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME, (void *)env_new, 0,
|
||||
CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to write env to %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
|
||||
|
@ -134,14 +134,14 @@ static int env_ubi_load(void)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
read1_fail = ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1,
|
||||
read1_fail = ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1, 0,
|
||||
CONFIG_ENV_SIZE);
|
||||
if (read1_fail)
|
||||
printf("\n** Unable to read env from %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
|
||||
|
||||
read2_fail = ubi_volume_read(CONFIG_ENV_UBI_VOLUME_REDUND,
|
||||
(void *)tmp_env2, CONFIG_ENV_SIZE);
|
||||
(void *)tmp_env2, 0, CONFIG_ENV_SIZE);
|
||||
if (read2_fail)
|
||||
printf("\n** Unable to read redundant env from %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME_REDUND);
|
||||
|
@ -171,7 +171,7 @@ static int env_ubi_load(void)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, buf, CONFIG_ENV_SIZE)) {
|
||||
if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, buf, 0, CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to read env from %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
|
||||
env_set_default(NULL, 0);
|
||||
|
@ -196,7 +196,7 @@ static int env_ubi_erase(void)
|
|||
memset(env_buf, 0x0, CONFIG_ENV_SIZE);
|
||||
|
||||
if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME,
|
||||
(void *)env_buf, CONFIG_ENV_SIZE)) {
|
||||
(void *)env_buf, 0, CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to erase env to %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART,
|
||||
CONFIG_ENV_UBI_VOLUME);
|
||||
|
@ -204,7 +204,7 @@ static int env_ubi_erase(void)
|
|||
}
|
||||
if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
|
||||
if (ubi_volume_write(ENV_UBI_VOLUME_REDUND,
|
||||
(void *)env_buf, CONFIG_ENV_SIZE)) {
|
||||
(void *)env_buf, 0, CONFIG_ENV_SIZE)) {
|
||||
printf("\n** Unable to erase env to %s:%s **\n",
|
||||
CONFIG_ENV_UBI_PART,
|
||||
ENV_UBI_VOLUME_REDUND);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <dm/device.h>
|
||||
#endif
|
||||
#include <dm/ofnode.h>
|
||||
#include <blk.h>
|
||||
|
||||
#define MAX_MTD_DEVICES 32
|
||||
#endif
|
||||
|
@ -412,6 +413,30 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
|||
int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
const u_char *buf);
|
||||
|
||||
#if CONFIG_IS_ENABLED(MTD_BLOCK)
|
||||
static inline struct mtd_info *blk_desc_to_mtd(struct blk_desc *bdesc)
|
||||
{
|
||||
void *priv = dev_get_priv(bdesc->bdev);
|
||||
|
||||
if (!priv)
|
||||
return NULL;
|
||||
|
||||
return *((struct mtd_info **)priv);
|
||||
}
|
||||
|
||||
int mtd_bind(struct udevice *dev, struct mtd_info **mtd);
|
||||
#else
|
||||
static inline struct mtd_info *blk_desc_to_mtd(struct blk_desc *bdesc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int mtd_bind(struct udevice *dev, struct mtd_info **mtd)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
|
||||
int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);
|
||||
|
||||
|
|
|
@ -30,12 +30,17 @@ struct block_drvr {
|
|||
#define PART_TYPE_ISO 0x03
|
||||
#define PART_TYPE_AMIGA 0x04
|
||||
#define PART_TYPE_EFI 0x05
|
||||
#define PART_TYPE_MTD 0x06
|
||||
#define PART_TYPE_UBI 0x07
|
||||
|
||||
/* maximum number of partition entries supported by search */
|
||||
#define DOS_ENTRY_NUMBERS 8
|
||||
#define ISO_ENTRY_NUMBERS 64
|
||||
#define MAC_ENTRY_NUMBERS 64
|
||||
#define AMIGA_ENTRY_NUMBERS 8
|
||||
#define MTD_ENTRY_NUMBERS 64
|
||||
#define UBI_ENTRY_NUMBERS UBI_MAX_VOLUMES
|
||||
|
||||
/*
|
||||
* Type string for U-Boot bootable partitions
|
||||
*/
|
||||
|
|
|
@ -48,11 +48,20 @@ extern int ubi_mtd_param_parse(const char *val, struct kernel_param *kp);
|
|||
extern int ubi_init(void);
|
||||
extern void ubi_exit(void);
|
||||
extern int ubi_part(char *part_name, const char *vid_header_offset);
|
||||
extern int ubi_volume_write(char *volume, void *buf, size_t size);
|
||||
extern int ubi_volume_read(char *volume, char *buf, size_t size);
|
||||
extern int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size);
|
||||
extern int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size);
|
||||
|
||||
extern struct ubi_device *ubi_devices[];
|
||||
int cmd_ubifs_mount(char *vol_name);
|
||||
int cmd_ubifs_umount(void);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UBI_BLOCK)
|
||||
int ubi_bind(struct udevice *dev);
|
||||
#else
|
||||
static inline int ubi_bind(struct udevice *dev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue