Merge branch 'next'
[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) ||
31             !CONFIG_IS_ENABLED(HAVE_BLOCK_DEVICE))
32                 return 0;
33
34         if (device_get_uclass_id(blk_dev) != UCLASS_BLK)
35                 return 0;
36
37         /* Add devices for each partition */
38         for (count = 0, part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
39                 if (part_get_info(desc, part, &info))
40                         continue;
41                 snprintf(devname, sizeof(devname), "%s:%d", blk_dev->name,
42                          part);
43
44                 ret = device_bind_driver(blk_dev, "blk_partition",
45                                          strdup(devname), &dev);
46                 if (ret)
47                         return ret;
48
49                 part_data = dev_get_uclass_plat(dev);
50                 part_data->partnum = part;
51                 part_data->gpt_part_info = info;
52                 count++;
53
54                 ret = device_probe(dev);
55                 if (ret) {
56                         debug("Can't probe\n");
57                         count--;
58                         device_unbind(dev);
59
60                         continue;
61                 }
62         }
63         debug("%s: %d partitions found in %s\n", __func__, count,
64               blk_dev->name);
65
66         return 0;
67 }
68
69 static ulong blk_part_read(struct udevice *dev, lbaint_t start,
70                            lbaint_t blkcnt, void *buffer)
71 {
72         struct udevice *parent;
73         struct disk_part *part;
74         const struct blk_ops *ops;
75
76         parent = dev_get_parent(dev);
77         ops = blk_get_ops(parent);
78         if (!ops->read)
79                 return -ENOSYS;
80
81         part = dev_get_uclass_plat(dev);
82         if (start >= part->gpt_part_info.size)
83                 return 0;
84
85         if ((start + blkcnt) > part->gpt_part_info.size)
86                 blkcnt = part->gpt_part_info.size - start;
87         start += part->gpt_part_info.start;
88
89         return ops->read(parent, start, blkcnt, buffer);
90 }
91
92 static ulong blk_part_write(struct udevice *dev, lbaint_t start,
93                             lbaint_t blkcnt, const void *buffer)
94 {
95         struct udevice *parent;
96         struct disk_part *part;
97         const struct blk_ops *ops;
98
99         parent = dev_get_parent(dev);
100         ops = blk_get_ops(parent);
101         if (!ops->write)
102                 return -ENOSYS;
103
104         part = dev_get_uclass_plat(dev);
105         if (start >= part->gpt_part_info.size)
106                 return 0;
107
108         if ((start + blkcnt) > part->gpt_part_info.size)
109                 blkcnt = part->gpt_part_info.size - start;
110         start += part->gpt_part_info.start;
111
112         return ops->write(parent, start, blkcnt, buffer);
113 }
114
115 static ulong blk_part_erase(struct udevice *dev, lbaint_t start,
116                             lbaint_t blkcnt)
117 {
118         struct udevice *parent;
119         struct disk_part *part;
120         const struct blk_ops *ops;
121
122         parent = dev_get_parent(dev);
123         ops = blk_get_ops(parent);
124         if (!ops->erase)
125                 return -ENOSYS;
126
127         part = dev_get_uclass_plat(dev);
128         if (start >= part->gpt_part_info.size)
129                 return 0;
130
131         if ((start + blkcnt) > part->gpt_part_info.size)
132                 blkcnt = part->gpt_part_info.size - start;
133         start += part->gpt_part_info.start;
134
135         return ops->erase(parent, start, blkcnt);
136 }
137
138 static const struct blk_ops blk_part_ops = {
139         .read   = blk_part_read,
140         .write  = blk_part_write,
141         .erase  = blk_part_erase,
142 };
143
144 U_BOOT_DRIVER(blk_partition) = {
145         .name           = "blk_partition",
146         .id             = UCLASS_PARTITION,
147         .ops            = &blk_part_ops,
148 };
149
150 /*
151  * BLOCK IO APIs
152  */
153 static struct blk_desc *dev_get_blk(struct udevice *dev)
154 {
155         struct blk_desc *block_dev;
156
157         switch (device_get_uclass_id(dev)) {
158         /*
159          * We won't support UCLASS_BLK with dev_* interfaces.
160          */
161         case UCLASS_PARTITION:
162                 block_dev = dev_get_uclass_plat(dev_get_parent(dev));
163                 break;
164         default:
165                 block_dev = NULL;
166                 break;
167         }
168
169         return block_dev;
170 }
171
172 unsigned long dev_read(struct udevice *dev, lbaint_t start,
173                        lbaint_t blkcnt, void *buffer)
174 {
175         struct blk_desc *block_dev;
176         const struct blk_ops *ops;
177         struct disk_part *part;
178         lbaint_t start_in_disk;
179         ulong blks_read;
180
181         block_dev = dev_get_blk(dev);
182         if (!block_dev)
183                 return -ENOSYS;
184
185         ops = blk_get_ops(dev);
186         if (!ops->read)
187                 return -ENOSYS;
188
189         start_in_disk = start;
190         if (device_get_uclass_id(dev) == UCLASS_PARTITION) {
191                 part = dev_get_uclass_plat(dev);
192                 start_in_disk += part->gpt_part_info.start;
193         }
194
195         if (blkcache_read(block_dev->if_type, block_dev->devnum,
196                           start_in_disk, blkcnt, block_dev->blksz, buffer))
197                 return blkcnt;
198         blks_read = ops->read(dev, start, blkcnt, buffer);
199         if (blks_read == blkcnt)
200                 blkcache_fill(block_dev->if_type, block_dev->devnum,
201                               start_in_disk, blkcnt, block_dev->blksz, buffer);
202
203         return blks_read;
204 }
205
206 unsigned long dev_write(struct udevice *dev, lbaint_t start,
207                         lbaint_t blkcnt, const void *buffer)
208 {
209         struct blk_desc *block_dev;
210         const struct blk_ops *ops;
211
212         block_dev = dev_get_blk(dev);
213         if (!block_dev)
214                 return -ENOSYS;
215
216         ops = blk_get_ops(dev);
217         if (!ops->write)
218                 return -ENOSYS;
219
220         blkcache_invalidate(block_dev->if_type, block_dev->devnum);
221
222         return ops->write(dev, start, blkcnt, buffer);
223 }
224
225 unsigned long dev_erase(struct udevice *dev, lbaint_t start,
226                         lbaint_t blkcnt)
227 {
228         struct blk_desc *block_dev;
229         const struct blk_ops *ops;
230
231         block_dev = dev_get_blk(dev);
232         if (!block_dev)
233                 return -ENOSYS;
234
235         ops = blk_get_ops(dev);
236         if (!ops->erase)
237                 return -ENOSYS;
238
239         blkcache_invalidate(block_dev->if_type, block_dev->devnum);
240
241         return ops->erase(dev, start, blkcnt);
242 }
243
244 UCLASS_DRIVER(partition) = {
245         .id             = UCLASS_PARTITION,
246         .per_device_plat_auto   = sizeof(struct disk_part),
247         .name           = "partition",
248 };