diff options
Diffstat (limited to 'disk/disk-uclass.c')
-rw-r--r-- | disk/disk-uclass.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/disk/disk-uclass.c b/disk/disk-uclass.c new file mode 100644 index 00000000000..72ff62ebf58 --- /dev/null +++ b/disk/disk-uclass.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Software partition device (UCLASS_PARTITION) + * + * Copyright (c) 2021 Linaro Limited + * Author: AKASHI Takahiro + */ + +#define LOG_CATEGORY UCLASS_PARTITION + +#include <blk.h> +#include <dm.h> +#include <log.h> +#include <part.h> +#include <vsprintf.h> +#include <dm/device-internal.h> +#include <dm/lists.h> + +int part_create_block_devices(struct udevice *blk_dev) +{ + int part, count; + struct blk_desc *desc = dev_get_uclass_plat(blk_dev); + struct disk_partition info; + struct disk_part *part_data; + char devname[32]; + struct udevice *dev; + int ret; + + if (!CONFIG_IS_ENABLED(PARTITIONS) || + !CONFIG_IS_ENABLED(HAVE_BLOCK_DEVICE)) + return 0; + + if (device_get_uclass_id(blk_dev) != UCLASS_BLK) + return 0; + + /* Add devices for each partition */ + for (count = 0, part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { + if (part_get_info(desc, part, &info)) + continue; + snprintf(devname, sizeof(devname), "%s:%d", blk_dev->name, + part); + + ret = device_bind_driver(blk_dev, "blk_partition", + strdup(devname), &dev); + if (ret) + return ret; + + part_data = dev_get_uclass_plat(dev); + part_data->partnum = part; + part_data->gpt_part_info = info; + count++; + + ret = device_probe(dev); + if (ret) { + debug("Can't probe\n"); + count--; + device_unbind(dev); + + continue; + } + } + debug("%s: %d partitions found in %s\n", __func__, count, + blk_dev->name); + + return 0; +} + +static ulong blk_part_read(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt, void *buffer) +{ + struct udevice *parent; + struct disk_part *part; + const struct blk_ops *ops; + + parent = dev_get_parent(dev); + ops = blk_get_ops(parent); + if (!ops->read) + return -ENOSYS; + + part = dev_get_uclass_plat(dev); + if (start >= part->gpt_part_info.size) + return 0; + + if ((start + blkcnt) > part->gpt_part_info.size) + blkcnt = part->gpt_part_info.size - start; + start += part->gpt_part_info.start; + + return ops->read(parent, start, blkcnt, buffer); +} + +static ulong blk_part_write(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt, const void *buffer) +{ + struct udevice *parent; + struct disk_part *part; + const struct blk_ops *ops; + + parent = dev_get_parent(dev); + ops = blk_get_ops(parent); + if (!ops->write) + return -ENOSYS; + + part = dev_get_uclass_plat(dev); + if (start >= part->gpt_part_info.size) + return 0; + + if ((start + blkcnt) > part->gpt_part_info.size) + blkcnt = part->gpt_part_info.size - start; + start += part->gpt_part_info.start; + + return ops->write(parent, start, blkcnt, buffer); +} + +static ulong blk_part_erase(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt) +{ + struct udevice *parent; + struct disk_part *part; + const struct blk_ops *ops; + + parent = dev_get_parent(dev); + ops = blk_get_ops(parent); + if (!ops->erase) + return -ENOSYS; + + part = dev_get_uclass_plat(dev); + if (start >= part->gpt_part_info.size) + return 0; + + if ((start + blkcnt) > part->gpt_part_info.size) + blkcnt = part->gpt_part_info.size - start; + start += part->gpt_part_info.start; + + return ops->erase(parent, start, blkcnt); +} + +static const struct blk_ops blk_part_ops = { + .read = blk_part_read, + .write = blk_part_write, + .erase = blk_part_erase, +}; + +U_BOOT_DRIVER(blk_partition) = { + .name = "blk_partition", + .id = UCLASS_PARTITION, + .ops = &blk_part_ops, +}; + +/* + * BLOCK IO APIs + */ +static struct blk_desc *dev_get_blk(struct udevice *dev) +{ + struct blk_desc *block_dev; + + switch (device_get_uclass_id(dev)) { + /* + * We won't support UCLASS_BLK with dev_* interfaces. + */ + case UCLASS_PARTITION: + block_dev = dev_get_uclass_plat(dev_get_parent(dev)); + break; + default: + block_dev = NULL; + break; + } + + return block_dev; +} + +unsigned long dev_read(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt, void *buffer) +{ + struct blk_desc *block_dev; + const struct blk_ops *ops; + struct disk_part *part; + lbaint_t start_in_disk; + ulong blks_read; + + block_dev = dev_get_blk(dev); + if (!block_dev) + return -ENOSYS; + + ops = blk_get_ops(dev); + if (!ops->read) + return -ENOSYS; + + start_in_disk = start; + if (device_get_uclass_id(dev) == UCLASS_PARTITION) { + part = dev_get_uclass_plat(dev); + start_in_disk += part->gpt_part_info.start; + } + + if (blkcache_read(block_dev->if_type, block_dev->devnum, + start_in_disk, blkcnt, block_dev->blksz, buffer)) + return blkcnt; + blks_read = ops->read(dev, start, blkcnt, buffer); + if (blks_read == blkcnt) + blkcache_fill(block_dev->if_type, block_dev->devnum, + start_in_disk, blkcnt, block_dev->blksz, buffer); + + return blks_read; +} + +unsigned long dev_write(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt, const void *buffer) +{ + struct blk_desc *block_dev; + const struct blk_ops *ops; + + block_dev = dev_get_blk(dev); + if (!block_dev) + return -ENOSYS; + + ops = blk_get_ops(dev); + if (!ops->write) + return -ENOSYS; + + blkcache_invalidate(block_dev->if_type, block_dev->devnum); + + return ops->write(dev, start, blkcnt, buffer); +} + +unsigned long dev_erase(struct udevice *dev, lbaint_t start, + lbaint_t blkcnt) +{ + struct blk_desc *block_dev; + const struct blk_ops *ops; + + block_dev = dev_get_blk(dev); + if (!block_dev) + return -ENOSYS; + + ops = blk_get_ops(dev); + if (!ops->erase) + return -ENOSYS; + + blkcache_invalidate(block_dev->if_type, block_dev->devnum); + + return ops->erase(dev, start, blkcnt); +} + +UCLASS_DRIVER(partition) = { + .id = UCLASS_PARTITION, + .per_device_plat_auto = sizeof(struct disk_part), + .name = "partition", +}; |