blob: 3bfb828044ccf13840b1e5bf4777c7226eed0d22 [file] [log] [blame]
AKASHI Takahiroefb9a9d2022-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 Schuchardt688dada2022-07-02 14:07:48 +020011#include <common.h>
AKASHI Takahiroefb9a9d2022-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
Simon Glass3bf7d7a2022-08-11 19:34:48 -060030 if (!CONFIG_IS_ENABLED(PARTITIONS) || !blk_enabled())
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +090031 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
Simon Glass4e93eea2022-10-20 18:22:52 -060068static ulong part_blk_read(struct udevice *dev, lbaint_t start,
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +090069 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
Simon Glass4e93eea2022-10-20 18:22:52 -060091static ulong part_blk_write(struct udevice *dev, lbaint_t start,
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +090092 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
Simon Glass4e93eea2022-10-20 18:22:52 -0600114static ulong part_blk_erase(struct udevice *dev, lbaint_t start,
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +0900115 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
137static const struct blk_ops blk_part_ops = {
Simon Glass4e93eea2022-10-20 18:22:52 -0600138 .read = part_blk_read,
139 .write = part_blk_write,
140 .erase = part_blk_erase,
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +0900141};
142
143U_BOOT_DRIVER(blk_partition) = {
144 .name = "blk_partition",
145 .id = UCLASS_PARTITION,
146 .ops = &blk_part_ops,
147};
148
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900149/*
150 * BLOCK IO APIs
151 */
152static struct blk_desc *dev_get_blk(struct udevice *dev)
153{
154 struct blk_desc *block_dev;
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 block_dev = dev_get_uclass_plat(dev_get_parent(dev));
162 break;
163 default:
164 block_dev = NULL;
165 break;
166 }
167
168 return block_dev;
169}
170
Simon Glass4e93eea2022-10-20 18:22:52 -0600171unsigned long disk_blk_read(struct udevice *dev, lbaint_t start,
172 lbaint_t blkcnt, void *buffer)
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900173{
174 struct blk_desc *block_dev;
175 const struct blk_ops *ops;
176 struct disk_part *part;
177 lbaint_t start_in_disk;
178 ulong blks_read;
179
180 block_dev = dev_get_blk(dev);
181 if (!block_dev)
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
Simon Glassfada3f92022-09-17 09:00:09 -0600194 if (blkcache_read(block_dev->uclass_id, block_dev->devnum,
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900195 start_in_disk, blkcnt, block_dev->blksz, buffer))
196 return blkcnt;
197 blks_read = ops->read(dev, start, blkcnt, buffer);
198 if (blks_read == blkcnt)
Simon Glassfada3f92022-09-17 09:00:09 -0600199 blkcache_fill(block_dev->uclass_id, block_dev->devnum,
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900200 start_in_disk, blkcnt, block_dev->blksz, buffer);
201
202 return blks_read;
203}
204
Simon Glass4e93eea2022-10-20 18:22:52 -0600205unsigned long disk_blk_write(struct udevice *dev, lbaint_t start,
206 lbaint_t blkcnt, const void *buffer)
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900207{
208 struct blk_desc *block_dev;
209 const struct blk_ops *ops;
210
211 block_dev = dev_get_blk(dev);
212 if (!block_dev)
213 return -ENOSYS;
214
215 ops = blk_get_ops(dev);
216 if (!ops->write)
217 return -ENOSYS;
218
Simon Glassfada3f92022-09-17 09:00:09 -0600219 blkcache_invalidate(block_dev->uclass_id, block_dev->devnum);
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900220
221 return ops->write(dev, start, blkcnt, buffer);
222}
223
Simon Glass4e93eea2022-10-20 18:22:52 -0600224unsigned long disk_blk_erase(struct udevice *dev, lbaint_t start,
225 lbaint_t blkcnt)
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900226{
227 struct blk_desc *block_dev;
228 const struct blk_ops *ops;
229
230 block_dev = dev_get_blk(dev);
231 if (!block_dev)
232 return -ENOSYS;
233
234 ops = blk_get_ops(dev);
235 if (!ops->erase)
236 return -ENOSYS;
237
Simon Glassfada3f92022-09-17 09:00:09 -0600238 blkcache_invalidate(block_dev->uclass_id, block_dev->devnum);
AKASHI Takahiro8b57fe32022-04-19 10:05:16 +0900239
240 return ops->erase(dev, start, blkcnt);
241}
242
AKASHI Takahiroefb9a9d2022-04-19 10:05:09 +0900243UCLASS_DRIVER(partition) = {
244 .id = UCLASS_PARTITION,
245 .per_device_plat_auto = sizeof(struct disk_part),
246 .name = "partition",
247};