blob: f3fb942a6b94cc46285aff2581a67e00d39ebc41 [file] [log] [blame]
AKASHI Takahiro43855fd2022-04-19 10:05:09 +09001// 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
Heinrich Schuchardt054de212022-07-02 14:07:48 +020011#include <common.h>
AKASHI Takahiro43855fd2022-04-19 10:05:09 +090012#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
20int 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
69static 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
92static 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
115static 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
138static const struct blk_ops blk_part_ops = {
139 .read = blk_part_read,
140 .write = blk_part_write,
141 .erase = blk_part_erase,
142};
143
144U_BOOT_DRIVER(blk_partition) = {
145 .name = "blk_partition",
146 .id = UCLASS_PARTITION,
147 .ops = &blk_part_ops,
148};
149
AKASHI Takahiro59da9d42022-04-19 10:05:16 +0900150/*
151 * BLOCK IO APIs
152 */
153static 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
172unsigned 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
206unsigned 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
225unsigned 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
AKASHI Takahiro43855fd2022-04-19 10:05:09 +0900244UCLASS_DRIVER(partition) = {
245 .id = UCLASS_PARTITION,
246 .per_device_plat_auto = sizeof(struct disk_part),
247 .name = "partition",
248};