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