1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2024 SaluteDevices, Inc.
4  *
5  * Author: Alexey Romanov <avromanov@salutedevices.com>
6  */
7 
8 #include <blk.h>
9 #include <part.h>
10 #include <ubi_uboot.h>
11 #include <dm/device.h>
12 #include <dm/device-internal.h>
13 
ubi_bind(struct udevice * dev)14 int ubi_bind(struct udevice *dev)
15 {
16 	struct blk_desc *bdesc;
17 	struct udevice *bdev;
18 	int ret;
19 
20 	ret = blk_create_devicef(dev, "ubi_blk", "blk", UCLASS_MTD,
21 				 -1, 512, 0, &bdev);
22 	if (ret) {
23 		pr_err("Cannot create block device");
24 		return ret;
25 	}
26 
27 	bdesc = dev_get_uclass_plat(bdev);
28 
29 	bdesc->bdev = bdev;
30 	bdesc->part_type = PART_TYPE_UBI;
31 
32 	return 0;
33 }
34 
get_ubi_device(void)35 static struct ubi_device *get_ubi_device(void)
36 {
37 	return ubi_devices[0];
38 }
39 
get_volume_name(int vol_id)40 static char *get_volume_name(int vol_id)
41 {
42 	struct ubi_device *ubi = get_ubi_device();
43 	int i;
44 
45 	for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
46 		struct ubi_volume *volume = ubi->volumes[i];
47 
48 		if (!volume)
49 			continue;
50 
51 		if (volume->vol_id >= UBI_INTERNAL_VOL_START)
52 			continue;
53 
54 		if (volume->vol_id == vol_id)
55 			return volume->name;
56 	}
57 
58 	return NULL;
59 }
60 
ubi_bread(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * dst)61 static ulong ubi_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
62 		       void *dst)
63 {
64 	struct blk_desc *block_dev = dev_get_uclass_plat(dev);
65 	char *volume_name = get_volume_name(block_dev->hwpart);
66 	unsigned int size = blkcnt * block_dev->blksz;
67 	loff_t offset = start * block_dev->blksz;
68 	int ret;
69 
70 	if (!volume_name) {
71 		pr_err("%s: failed to find volume name for blk=" LBAF "\n", __func__, start);
72 		return -EINVAL;
73 	}
74 
75 	ret = ubi_volume_read(volume_name, dst, offset, size);
76 	if (ret) {
77 		pr_err("%s: failed to read from %s UBI volume\n", __func__, volume_name);
78 		return ret;
79 	}
80 
81 	return blkcnt;
82 }
83 
ubi_bwrite(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,const void * src)84 static ulong ubi_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
85 			const void *src)
86 {
87 	struct blk_desc *block_dev = dev_get_uclass_plat(dev);
88 	char *volume_name = get_volume_name(block_dev->hwpart);
89 	unsigned int size = blkcnt * block_dev->blksz;
90 	loff_t offset = start * block_dev->blksz;
91 	int ret;
92 
93 	if (!volume_name) {
94 		pr_err("%s: failed to find volume for blk=" LBAF "\n", __func__, start);
95 		return -EINVAL;
96 	}
97 
98 	ret = ubi_volume_write(volume_name, (void *)src, offset, size);
99 	if (ret) {
100 		pr_err("%s: failed to write from %s UBI volume\n", __func__, volume_name);
101 		return ret;
102 	}
103 
104 	return blkcnt;
105 }
106 
ubi_blk_probe(struct udevice * dev)107 static int ubi_blk_probe(struct udevice *dev)
108 {
109 	int ret;
110 
111 	ret = device_probe(dev);
112 	if (ret) {
113 		pr_err("Probing %s failed (err=%d)\n", dev->name, ret);
114 		return ret;
115 	}
116 
117 	return 0;
118 }
119 
120 static const struct blk_ops ubi_blk_ops = {
121 	.read = ubi_bread,
122 	.write = ubi_bwrite,
123 };
124 
125 U_BOOT_DRIVER(ubi_blk) = {
126 	.name = "ubi_blk",
127 	.id = UCLASS_BLK,
128 	.ops = &ubi_blk_ops,
129 	.probe = ubi_blk_probe,
130 };
131