[PATCH v2 0/3] Introduce mtdblock device

Hello!
This series adds support for the mtdblock device, which allows to read/write data block by block. For example, it can now be used for BCB or Android AB command:
$ bcb load mtd 0 part_name
Tested only on SPI NAND, so bind is made only for SPI NAND drivers.
---
Changes V1 -> V2 [1]:
- Drop patch [2]. - Add warning if bind NAND mtdblock device. - Move documentation of mtdblock implementation from commit message to the source code. - Remove __maybe_unused from mtd partition functions description. - Use blk_enabled() instead of #ifdefs.
Links:
- [1] https://lore.kernel.org/all/20240227100441.1811047-1-avromanov@salutedevices... - [2] https://lore.kernel.org/all/20240227100441.1811047-5-avromanov@salutedevices...
Alexey Romanov (3): disk: support MTD partitions drivers: introduce mtdblock abstraction spinand: bind mtdblock
disk/part.c | 5 +- drivers/block/blk-uclass.c | 1 + drivers/mtd/Kconfig | 1 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdblock.c | 227 ++++++++++++++++++++++++++++++++++++ drivers/mtd/mtdpart.c | 69 +++++++++++ drivers/mtd/nand/spi/core.c | 20 ++++ include/linux/mtd/mtd.h | 12 ++ include/part.h | 2 + 9 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/mtdblock.c

Add new MTD partition driver, which can be useful with mtdblock driver combination.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com --- disk/part.c | 5 +++- drivers/mtd/Kconfig | 1 + drivers/mtd/mtdpart.c | 69 +++++++++++++++++++++++++++++++++++++++++++ include/part.h | 2 ++ 4 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/disk/part.c b/disk/part.c index 36b88205ec..0fc5cc0419 100644 --- a/disk/part.c +++ b/disk/part.c @@ -304,7 +304,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) puts ("\nPartition Map for "); switch (desc->uclass_id) { case UCLASS_IDE: @@ -343,6 +344,8 @@ static void print_part_header(const char *type, struct blk_desc *desc) case UCLASS_BLKMAP: puts("BLKMAP"); break; + case UCLASS_MTD: + puts("MTD"); default: printf("UNKNOWN(%d)", desc->uclass_id); break; diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1902351719..40272f7e50 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" diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 4886392a1c..67244869f2 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -21,6 +21,8 @@
#include <common.h> #include <malloc.h> +#include <memalign.h> +#include <part.h> #include <linux/bug.h> #include <linux/errno.h> #include <linux/compat.h> @@ -1055,3 +1057,70 @@ 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 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) { + pr_err("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 part_print_mtd(struct blk_desc *dev_desc) +{ + struct mtd_info *master = blk_desc_to_mtd(dev_desc); + struct mtd_info *part; + + 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) +{ + 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(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/include/part.h b/include/part.h index db34bc6bb7..f7f3773a95 100644 --- a/include/part.h +++ b/include/part.h @@ -30,12 +30,14 @@ struct block_drvr { #define PART_TYPE_ISO 0x03 #define PART_TYPE_AMIGA 0x04 #define PART_TYPE_EFI 0x05 +#define PART_TYPE_MTD 0x06
/* 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 /* * Type string for U-Boot bootable partitions */

On 07.03.24 14:07, Alexey Romanov wrote:
Add new MTD partition driver, which can be useful with mtdblock driver combination.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
disk/part.c | 5 +++- drivers/mtd/Kconfig | 1 + drivers/mtd/mtdpart.c | 69 +++++++++++++++++++++++++++++++++++++++++++ include/part.h | 2 ++ 4 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/disk/part.c b/disk/part.c index 36b88205ec..0fc5cc0419 100644 --- a/disk/part.c +++ b/disk/part.c @@ -304,7 +304,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) puts ("\nPartition Map for "); switch (desc->uclass_id) { case UCLASS_IDE:
@@ -343,6 +344,8 @@ static void print_part_header(const char *type, struct blk_desc *desc) case UCLASS_BLKMAP: puts("BLKMAP"); break;
- case UCLASS_MTD:
default:puts("MTD");
The function should simply call uclass_get_name() instead of duplicating it:
[PATCH 1/1] disk: simplify print_part_header() https://lore.kernel.org/u-boot/20240403114047.84030-1-heinrich.schuchardt@ca...
printf("UNKNOWN(%d)", desc->uclass_id); break;
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1902351719..40272f7e50 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"
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 4886392a1c..67244869f2 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -21,6 +21,8 @@
#include <common.h> #include <malloc.h> +#include <memalign.h> +#include <part.h> #include <linux/bug.h> #include <linux/errno.h> #include <linux/compat.h> @@ -1055,3 +1057,70 @@ 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 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) {
pr_err("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 part_print_mtd(struct blk_desc *dev_desc) +{
- struct mtd_info *master = blk_desc_to_mtd(dev_desc);
- struct mtd_info *part;
- 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) +{
- 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(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/include/part.h b/include/part.h index db34bc6bb7..f7f3773a95 100644 --- a/include/part.h +++ b/include/part.h @@ -30,12 +30,14 @@ struct block_drvr { #define PART_TYPE_ISO 0x03 #define PART_TYPE_AMIGA 0x04 #define PART_TYPE_EFI 0x05 +#define PART_TYPE_MTD 0x06
/* 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
Please, add a blank line here.
Best regards
Heinrich
/*
- Type string for U-Boot bootable partitions
*/

MTD block - abstraction over MTD subsystem, allowing to read and write in blocks using BLK UCLASS.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com --- drivers/block/blk-uclass.c | 1 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdblock.c | 227 +++++++++++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 12 ++ 4 files changed, 241 insertions(+) create mode 100644 drivers/mtd/mtdblock.c
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 77066da352..ab0a9105c9 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -37,6 +37,7 @@ static struct { { UCLASS_PVBLOCK, "pvblock" }, { UCLASS_BLKMAP, "blkmap" }, { UCLASS_RKMTD, "rkmtd" }, + { UCLASS_MTD, "mtd" }, };
static enum uclass_id uclass_name_to_iftype(const char *uclass_idname) diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c638980ea2..993b122ac4 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_BLK) += mtdblock.o
#SPL/TPL build else diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c new file mode 100644 index 0000000000..b409674173 --- /dev/null +++ b/drivers/mtd/mtdblock.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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, + dev_seq(dev), 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_bread(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%lx\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_bwrite(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%lx\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_bread, + .write = mtd_bwrite, +}; + +U_BOOT_DRIVER(mtd_blk) = { + .name = "mtd_blk", + .id = UCLASS_BLK, + .ops = &mtd_blk_ops, + .probe = mtd_blk_probe, +}; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 09f5269887..9b997fadd1 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -26,6 +26,9 @@ #include <dm/device.h> #endif #include <dm/ofnode.h> +#if IS_ENABLED(CONFIG_BLK) +#include <blk.h> +#endif
#define MAX_MTD_DEVICES 32 #endif @@ -412,6 +415,15 @@ 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 IS_ENABLED(CONFIG_BLK) +static inline struct mtd_info *blk_desc_to_mtd(struct blk_desc *bdesc) +{ + return *((struct mtd_info **)dev_get_priv(bdesc->bdev)); +} + +int mtd_bind(struct udevice *dev, struct mtd_info **mtd); +#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);

On 07.03.24 14:07, Alexey Romanov wrote:
MTD block - abstraction over MTD subsystem, allowing to read and write in blocks using BLK UCLASS.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
drivers/block/blk-uclass.c | 1 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdblock.c | 227 +++++++++++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 12 ++ 4 files changed, 241 insertions(+) create mode 100644 drivers/mtd/mtdblock.c
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 77066da352..ab0a9105c9 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -37,6 +37,7 @@ static struct { { UCLASS_PVBLOCK, "pvblock" }, { UCLASS_BLKMAP, "blkmap" }, { UCLASS_RKMTD, "rkmtd" },
{ UCLASS_MTD, "mtd" }, };
static enum uclass_id uclass_name_to_iftype(const char *uclass_idname)
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c638980ea2..993b122ac4 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_BLK) += mtdblock.o
#SPL/TPL build else diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c new file mode 100644 index 0000000000..b409674173 --- /dev/null +++ b/drivers/mtd/mtdblock.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0+
nits:
GPL-2.0-or-later see https://spdx.org/licenses/GPL-2.0-or-later.html
+/*
- MTD block - abstraction over MTD subsystem, allowing
- to read and write in blocks using BLK UCLASS.
- Read algorithm:
- Convert start block number to start address.
- 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:
- Convert start block number to start address.
- Round this address down by mtd->erasesize.
- Erase addr Start addr
| |
v v
+----------------+----------------+----------------+
| blksz | blksz | blksz |
+----------------+----------------+----------------+
- Calculate offset between this two addresses.
- 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
- Copy data from user buffer to temporary buffer with offset,
calculated at step 3.
- Erase and write mtd->erasesize bytes at erase address
pointer using mtd_erase/mtd_write().
- Add to erase address pointer mtd->erasesize bytes.
- 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,
dev_seq(dev), 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_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
nits:
Reading bread I thought of nutrition. How about mtd_blk_read()?
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%lx\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_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
mtd_blk_write()?
Best regards
Heinrich
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%lx\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_bread,
- .write = mtd_bwrite,
+};
+U_BOOT_DRIVER(mtd_blk) = {
- .name = "mtd_blk",
- .id = UCLASS_BLK,
- .ops = &mtd_blk_ops,
- .probe = mtd_blk_probe,
+}; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 09f5269887..9b997fadd1 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -26,6 +26,9 @@ #include <dm/device.h> #endif #include <dm/ofnode.h> +#if IS_ENABLED(CONFIG_BLK) +#include <blk.h> +#endif
#define MAX_MTD_DEVICES 32 #endif @@ -412,6 +415,15 @@ 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 IS_ENABLED(CONFIG_BLK) +static inline struct mtd_info *blk_desc_to_mtd(struct blk_desc *bdesc) +{
- return *((struct mtd_info **)dev_get_priv(bdesc->bdev));
+}
+int mtd_bind(struct udevice *dev, struct mtd_info **mtd); +#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);

Bind SPI-NAND driver to MTD block driver.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com --- drivers/mtd/nand/spi/core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 2a3dbcfcb4..dd880adf31 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -36,6 +36,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;
@@ -1174,12 +1178,24 @@ 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); + + return mtd_bind(dev, &plat->mtd); + } + + 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__ @@ -1219,6 +1235,8 @@ static int spinand_probe(struct udevice *dev) if (ret) goto err_spinand_cleanup;
+ plat->mtd = mtd; + return 0;
err_spinand_cleanup: @@ -1288,6 +1306,8 @@ 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), };
void board_nand_init(void)

On 07.03.24 14:07, Alexey Romanov wrote:
Bind SPI-NAND driver to MTD block driver.
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Reviewed-by: Frieder Schrempf frieder.schrempf@kontron.de
drivers/mtd/nand/spi/core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 2a3dbcfcb4..dd880adf31 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -36,6 +36,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;
@@ -1174,12 +1178,24 @@ 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);
return mtd_bind(dev, &plat->mtd);
- }
- 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__ @@ -1219,6 +1235,8 @@ static int spinand_probe(struct udevice *dev) if (ret) goto err_spinand_cleanup;
- plat->mtd = mtd;
- return 0;
err_spinand_cleanup: @@ -1288,6 +1306,8 @@ 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),
};
void board_nand_init(void)

Hello! Ping.
On Thu, Mar 07, 2024 at 04:07:23PM +0300, Alexey Romanov wrote:
Hello!
This series adds support for the mtdblock device, which allows to read/write data block by block. For example, it can now be used for BCB or Android AB command:
$ bcb load mtd 0 part_name
Tested only on SPI NAND, so bind is made only for SPI NAND drivers.
Changes V1 -> V2 [1]:
- Drop patch [2].
- Add warning if bind NAND mtdblock device.
- Move documentation of mtdblock implementation from commit message to the source code.
- Remove __maybe_unused from mtd partition functions description.
- Use blk_enabled() instead of #ifdefs.
Links:
- [1] https://lore.kernel.org/all/20240227100441.1811047-1-avromanov@salutedevices...
- [2] https://lore.kernel.org/all/20240227100441.1811047-5-avromanov@salutedevices...
Alexey Romanov (3): disk: support MTD partitions drivers: introduce mtdblock abstraction spinand: bind mtdblock
disk/part.c | 5 +- drivers/block/blk-uclass.c | 1 + drivers/mtd/Kconfig | 1 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdblock.c | 227 ++++++++++++++++++++++++++++++++++++ drivers/mtd/mtdpart.c | 69 +++++++++++ drivers/mtd/nand/spi/core.c | 20 ++++ include/linux/mtd/mtd.h | 12 ++ include/part.h | 2 + 9 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/mtdblock.c
-- 2.34.1

Hi guys! Ping.
On Thu, Mar 07, 2024 at 04:07:23PM +0300, Alexey Romanov wrote:
Hello!
This series adds support for the mtdblock device, which allows to read/write data block by block. For example, it can now be used for BCB or Android AB command:
$ bcb load mtd 0 part_name
Tested only on SPI NAND, so bind is made only for SPI NAND drivers.
Changes V1 -> V2 [1]:
- Drop patch [2].
- Add warning if bind NAND mtdblock device.
- Move documentation of mtdblock implementation from commit message to the source code.
- Remove __maybe_unused from mtd partition functions description.
- Use blk_enabled() instead of #ifdefs.
Links:
- [1] https://lore.kernel.org/all/20240227100441.1811047-1-avromanov@salutedevices...
- [2] https://lore.kernel.org/all/20240227100441.1811047-5-avromanov@salutedevices...
Alexey Romanov (3): disk: support MTD partitions drivers: introduce mtdblock abstraction spinand: bind mtdblock
disk/part.c | 5 +- drivers/block/blk-uclass.c | 1 + drivers/mtd/Kconfig | 1 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdblock.c | 227 ++++++++++++++++++++++++++++++++++++ drivers/mtd/mtdpart.c | 69 +++++++++++ drivers/mtd/nand/spi/core.c | 20 ++++ include/linux/mtd/mtd.h | 12 ++ include/part.h | 2 + 9 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/mtdblock.c
-- 2.34.1
participants (3)
-
Alexey Romanov
-
Frieder Schrempf
-
Heinrich Schuchardt