Merge tag 'doc-2023-10-rc5-3' of https://source.denx.de/u-boot/custodians/u-boot-efi
[platform/kernel/u-boot.git] / disk / disk-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Software partition device (UCLASS_PARTITION)
4  *
5  *  Copyright (c) 2021 Linaro Limited
6  *                      Author: AKASHI Takahiro
7  */
8
9 #define LOG_CATEGORY UCLASS_PARTITION
10
11 #include <common.h>
12 #include <blk.h>
13 #include <dm.h>
14 #include <log.h>
15 #include <part.h>
16 #include <vsprintf.h>
17 #include <dm/device-internal.h>
18 #include <dm/lists.h>
19
20 int part_create_block_devices(struct udevice *blk_dev)
21 {
22         int part, count;
23         struct blk_desc *desc = dev_get_uclass_plat(blk_dev);
24         struct disk_partition info;
25         struct disk_part *part_data;
26         char devname[32];
27         struct udevice *dev;
28         int ret;
29
30         if (!CONFIG_IS_ENABLED(PARTITIONS) || !blk_enabled())
31                 return 0;
32
33         if (device_get_uclass_id(blk_dev) != UCLASS_BLK)
34                 return 0;
35
36         /* Add devices for each partition */
37         for (count = 0, part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
38                 if (part_get_info(desc, part, &info))
39                         continue;
40                 snprintf(devname, sizeof(devname), "%s:%d", blk_dev->name,
41                          part);
42
43                 ret = device_bind_driver(blk_dev, "blk_partition",
44                                          strdup(devname), &dev);
45                 if (ret)
46                         return ret;
47
48                 part_data = dev_get_uclass_plat(dev);
49                 part_data->partnum = part;
50                 part_data->gpt_part_info = info;
51                 count++;
52
53                 ret = device_probe(dev);
54                 if (ret) {
55                         debug("Can't probe\n");
56                         count--;
57                         device_unbind(dev);
58
59                         continue;
60                 }
61         }
62         debug("%s: %d partitions found in %s\n", __func__, count,
63               blk_dev->name);
64
65         return 0;
66 }
67
68 static ulong part_blk_read(struct udevice *dev, lbaint_t start,
69                            lbaint_t blkcnt, void *buffer)
70 {
71         struct udevice *parent;
72         struct disk_part *part;
73         const struct blk_ops *ops;
74
75         parent = dev_get_parent(dev);
76         ops = blk_get_ops(parent);
77         if (!ops->read)
78                 return -ENOSYS;
79
80         part = dev_get_uclass_plat(dev);
81         if (start >= part->gpt_part_info.size)
82                 return 0;
83
84         if ((start + blkcnt) > part->gpt_part_info.size)
85                 blkcnt = part->gpt_part_info.size - start;
86         start += part->gpt_part_info.start;
87
88         return ops->read(parent, start, blkcnt, buffer);
89 }
90
91 static ulong part_blk_write(struct udevice *dev, lbaint_t start,
92                             lbaint_t blkcnt, const void *buffer)
93 {
94         struct udevice *parent;
95         struct disk_part *part;
96         const struct blk_ops *ops;
97
98         parent = dev_get_parent(dev);
99         ops = blk_get_ops(parent);
100         if (!ops->write)
101                 return -ENOSYS;
102
103         part = dev_get_uclass_plat(dev);
104         if (start >= part->gpt_part_info.size)
105                 return 0;
106
107         if ((start + blkcnt) > part->gpt_part_info.size)
108                 blkcnt = part->gpt_part_info.size - start;
109         start += part->gpt_part_info.start;
110
111         return ops->write(parent, start, blkcnt, buffer);
112 }
113
114 static ulong part_blk_erase(struct udevice *dev, lbaint_t start,
115                             lbaint_t blkcnt)
116 {
117         struct udevice *parent;
118         struct disk_part *part;
119         const struct blk_ops *ops;
120
121         parent = dev_get_parent(dev);
122         ops = blk_get_ops(parent);
123         if (!ops->erase)
124                 return -ENOSYS;
125
126         part = dev_get_uclass_plat(dev);
127         if (start >= part->gpt_part_info.size)
128                 return 0;
129
130         if ((start + blkcnt) > part->gpt_part_info.size)
131                 blkcnt = part->gpt_part_info.size - start;
132         start += part->gpt_part_info.start;
133
134         return ops->erase(parent, start, blkcnt);
135 }
136
137 static const struct blk_ops blk_part_ops = {
138         .read   = part_blk_read,
139         .write  = part_blk_write,
140         .erase  = part_blk_erase,
141 };
142
143 U_BOOT_DRIVER(blk_partition) = {
144         .name           = "blk_partition",
145         .id             = UCLASS_PARTITION,
146         .ops            = &blk_part_ops,
147 };
148
149 /*
150  * BLOCK IO APIs
151  */
152 static struct blk_desc *dev_get_blk(struct udevice *dev)
153 {
154         struct blk_desc *desc;
155
156         switch (device_get_uclass_id(dev)) {
157         /*
158          * We won't support UCLASS_BLK with dev_* interfaces.
159          */
160         case UCLASS_PARTITION:
161                 desc = dev_get_uclass_plat(dev_get_parent(dev));
162                 break;
163         default:
164                 desc = NULL;
165                 break;
166         }
167
168         return desc;
169 }
170
171 unsigned long disk_blk_read(struct udevice *dev, lbaint_t start,
172                             lbaint_t blkcnt, void *buffer)
173 {
174         struct blk_desc *desc;
175         const struct blk_ops *ops;
176         struct disk_part *part;
177         lbaint_t start_in_disk;
178         ulong blks_read;
179
180         desc = dev_get_blk(dev);
181         if (!desc)
182                 return -ENOSYS;
183
184         ops = blk_get_ops(dev);
185         if (!ops->read)
186                 return -ENOSYS;
187
188         start_in_disk = start;
189         if (device_get_uclass_id(dev) == UCLASS_PARTITION) {
190                 part = dev_get_uclass_plat(dev);
191                 start_in_disk += part->gpt_part_info.start;
192         }
193
194         if (blkcache_read(desc->uclass_id, desc->devnum, start_in_disk, blkcnt,
195                           desc->blksz, buffer))
196                 return blkcnt;
197         blks_read = ops->read(dev, start, blkcnt, buffer);
198         if (blks_read == blkcnt)
199                 blkcache_fill(desc->uclass_id, desc->devnum, start_in_disk,
200                               blkcnt, desc->blksz, buffer);
201
202         return blks_read;
203 }
204
205 unsigned long disk_blk_write(struct udevice *dev, lbaint_t start,
206                              lbaint_t blkcnt, const void *buffer)
207 {
208         struct blk_desc *desc;
209         const struct blk_ops *ops;
210
211         desc = dev_get_blk(dev);
212         if (!desc)
213                 return -ENOSYS;
214
215         ops = blk_get_ops(dev);
216         if (!ops->write)
217                 return -ENOSYS;
218
219         blkcache_invalidate(desc->uclass_id, desc->devnum);
220
221         return ops->write(dev, start, blkcnt, buffer);
222 }
223
224 unsigned long disk_blk_erase(struct udevice *dev, lbaint_t start,
225                              lbaint_t blkcnt)
226 {
227         struct blk_desc *desc;
228         const struct blk_ops *ops;
229
230         desc = dev_get_blk(dev);
231         if (!desc)
232                 return -ENOSYS;
233
234         ops = blk_get_ops(dev);
235         if (!ops->erase)
236                 return -ENOSYS;
237
238         blkcache_invalidate(desc->uclass_id, desc->devnum);
239
240         return ops->erase(dev, start, blkcnt);
241 }
242
243 UCLASS_DRIVER(partition) = {
244         .id             = UCLASS_PARTITION,
245         .per_device_plat_auto   = sizeof(struct disk_part),
246         .name           = "partition",
247 };