131 lines
2.6 KiB
C
131 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2024 SaluteDevices, Inc.
|
|
*
|
|
* Author: Alexey Romanov <[email protected]>
|
|
*/
|
|
|
|
#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,
|
|
};
|