summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/ubi.c78
-rw-r--r--disk/part.c10
-rw-r--r--drivers/block/blk-uclass.c2
-rw-r--r--drivers/mtd/Kconfig8
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/mtdblock.c227
-rw-r--r--drivers/mtd/mtdpart.c76
-rw-r--r--drivers/mtd/nand/spi/core.c29
-rw-r--r--drivers/mtd/ubi/Kconfig6
-rw-r--r--drivers/mtd/ubi/Makefile1
-rw-r--r--drivers/mtd/ubi/block.c130
-rw-r--r--drivers/mtd/ubi/part.c99
-rw-r--r--env/ubi.c16
-rw-r--r--include/linux/mtd/mtd.h25
-rw-r--r--include/part.h5
-rw-r--r--include/ubi_uboot.h13
16 files changed, 709 insertions, 17 deletions
diff --git a/cmd/ubi.c b/cmd/ubi.c
index 92998af2b0..0e62e44932 100644
--- a/cmd/ubi.c
+++ b/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_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, size_t 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);
}
}
diff --git a/disk/part.c b/disk/part.c
index bc932526f9..706d77b319 100644
--- a/disk/part.c
+++ b/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 */
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index 512c952f4d..312e038445 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -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)
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 4fdc9645d0..3764e2567c 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -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
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index c2fc80b10f..10d575e9f9 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -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
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
new file mode 100644
index 0000000000..66a79b8c56
--- /dev/null
+++ b/drivers/mtd/mtdblock.c
@@ -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,
+};
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index be1d19b4ff..88094b81e7 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -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,
+};
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index ef50237f10..f5ddfbf4b8 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -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),
};
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index fd446d6efb..c027d898a6 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -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"
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index 30d00fbdfe..690ef9e901 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -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
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
new file mode 100644
index 0000000000..99d55282cd
--- /dev/null
+++ b/drivers/mtd/ubi/block.c
@@ -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,
+};
diff --git a/drivers/mtd/ubi/part.c b/drivers/mtd/ubi/part.c
new file mode 100644
index 0000000000..13d1f165c3
--- /dev/null
+++ b/drivers/mtd/ubi/part.c
@@ -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,
+};
diff --git a/env/ubi.c b/env/ubi.c
index 0c3e93c2bf..2f4ca571ed 100644
--- a/env/ubi.c
+++ b/env/ubi.c
@@ -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);
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 983a55ce70..6751fb52c5 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -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);
diff --git a/include/part.h b/include/part.h
index b187ec4b4b..54b986cee6 100644
--- a/include/part.h
+++ b/include/part.h
@@ -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
*/
diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
index d7a8851094..ea0db69c72 100644
--- a/include/ubi_uboot.h
+++ b/include/ubi_uboot.h
@@ -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