]>
Commit | Line | Data |
---|---|---|
43855fdb AT |
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 | ||
054de212 | 11 | #include <common.h> |
43855fdb AT |
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 | ||
7f8967c2 | 30 | if (!CONFIG_IS_ENABLED(PARTITIONS) || !blk_enabled()) |
43855fdb AT |
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 | ||
d87bdb82 SG |
68 | static int blk_part_setup(struct udevice *dev, lbaint_t *startp, |
69 | lbaint_t blkcnt) | |
70 | { | |
71 | struct disk_part *part; | |
72 | ||
73 | part = dev_get_uclass_plat(dev); | |
74 | if (*startp >= part->gpt_part_info.size) | |
75 | return -E2BIG; | |
76 | ||
77 | if (*startp + blkcnt > part->gpt_part_info.size) | |
78 | blkcnt = part->gpt_part_info.size - *startp; | |
79 | *startp += part->gpt_part_info.start; | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
76c839fc | 84 | static ulong part_blk_read(struct udevice *dev, lbaint_t start, |
43855fdb AT |
85 | lbaint_t blkcnt, void *buffer) |
86 | { | |
87 | struct udevice *parent; | |
43855fdb | 88 | const struct blk_ops *ops; |
d87bdb82 | 89 | int ret; |
43855fdb AT |
90 | |
91 | parent = dev_get_parent(dev); | |
92 | ops = blk_get_ops(parent); | |
93 | if (!ops->read) | |
94 | return -ENOSYS; | |
95 | ||
d87bdb82 SG |
96 | ret = blk_part_setup(dev, &start, blkcnt); |
97 | if (ret) | |
43855fdb AT |
98 | return 0; |
99 | ||
43855fdb AT |
100 | return ops->read(parent, start, blkcnt, buffer); |
101 | } | |
102 | ||
76c839fc | 103 | static ulong part_blk_write(struct udevice *dev, lbaint_t start, |
43855fdb AT |
104 | lbaint_t blkcnt, const void *buffer) |
105 | { | |
106 | struct udevice *parent; | |
43855fdb | 107 | const struct blk_ops *ops; |
d87bdb82 | 108 | int ret; |
43855fdb AT |
109 | |
110 | parent = dev_get_parent(dev); | |
111 | ops = blk_get_ops(parent); | |
112 | if (!ops->write) | |
113 | return -ENOSYS; | |
114 | ||
d87bdb82 SG |
115 | ret = blk_part_setup(dev, &start, blkcnt); |
116 | if (ret) | |
43855fdb AT |
117 | return 0; |
118 | ||
43855fdb AT |
119 | return ops->write(parent, start, blkcnt, buffer); |
120 | } | |
121 | ||
76c839fc | 122 | static ulong part_blk_erase(struct udevice *dev, lbaint_t start, |
43855fdb AT |
123 | lbaint_t blkcnt) |
124 | { | |
125 | struct udevice *parent; | |
43855fdb | 126 | const struct blk_ops *ops; |
d87bdb82 | 127 | int ret; |
43855fdb AT |
128 | |
129 | parent = dev_get_parent(dev); | |
130 | ops = blk_get_ops(parent); | |
131 | if (!ops->erase) | |
132 | return -ENOSYS; | |
133 | ||
d87bdb82 SG |
134 | ret = blk_part_setup(dev, &start, blkcnt); |
135 | if (ret) | |
43855fdb AT |
136 | return 0; |
137 | ||
43855fdb AT |
138 | return ops->erase(parent, start, blkcnt); |
139 | } | |
140 | ||
141 | static const struct blk_ops blk_part_ops = { | |
76c839fc SG |
142 | .read = part_blk_read, |
143 | .write = part_blk_write, | |
144 | .erase = part_blk_erase, | |
43855fdb AT |
145 | }; |
146 | ||
147 | U_BOOT_DRIVER(blk_partition) = { | |
148 | .name = "blk_partition", | |
149 | .id = UCLASS_PARTITION, | |
150 | .ops = &blk_part_ops, | |
151 | }; | |
152 | ||
59da9d47 AT |
153 | /* |
154 | * BLOCK IO APIs | |
155 | */ | |
156 | static struct blk_desc *dev_get_blk(struct udevice *dev) | |
157 | { | |
b55afa0c | 158 | struct blk_desc *desc; |
59da9d47 AT |
159 | |
160 | switch (device_get_uclass_id(dev)) { | |
161 | /* | |
162 | * We won't support UCLASS_BLK with dev_* interfaces. | |
163 | */ | |
164 | case UCLASS_PARTITION: | |
b55afa0c | 165 | desc = dev_get_uclass_plat(dev_get_parent(dev)); |
59da9d47 AT |
166 | break; |
167 | default: | |
b55afa0c | 168 | desc = NULL; |
59da9d47 AT |
169 | break; |
170 | } | |
171 | ||
b55afa0c | 172 | return desc; |
59da9d47 AT |
173 | } |
174 | ||
76c839fc SG |
175 | unsigned long disk_blk_read(struct udevice *dev, lbaint_t start, |
176 | lbaint_t blkcnt, void *buffer) | |
59da9d47 | 177 | { |
b55afa0c | 178 | struct blk_desc *desc; |
59da9d47 AT |
179 | const struct blk_ops *ops; |
180 | struct disk_part *part; | |
181 | lbaint_t start_in_disk; | |
182 | ulong blks_read; | |
183 | ||
b55afa0c SG |
184 | desc = dev_get_blk(dev); |
185 | if (!desc) | |
59da9d47 AT |
186 | return -ENOSYS; |
187 | ||
188 | ops = blk_get_ops(dev); | |
189 | if (!ops->read) | |
190 | return -ENOSYS; | |
191 | ||
192 | start_in_disk = start; | |
193 | if (device_get_uclass_id(dev) == UCLASS_PARTITION) { | |
194 | part = dev_get_uclass_plat(dev); | |
195 | start_in_disk += part->gpt_part_info.start; | |
196 | } | |
197 | ||
b55afa0c SG |
198 | if (blkcache_read(desc->uclass_id, desc->devnum, start_in_disk, blkcnt, |
199 | desc->blksz, buffer)) | |
59da9d47 AT |
200 | return blkcnt; |
201 | blks_read = ops->read(dev, start, blkcnt, buffer); | |
202 | if (blks_read == blkcnt) | |
b55afa0c SG |
203 | blkcache_fill(desc->uclass_id, desc->devnum, start_in_disk, |
204 | blkcnt, desc->blksz, buffer); | |
59da9d47 AT |
205 | |
206 | return blks_read; | |
207 | } | |
208 | ||
76c839fc SG |
209 | unsigned long disk_blk_write(struct udevice *dev, lbaint_t start, |
210 | lbaint_t blkcnt, const void *buffer) | |
59da9d47 | 211 | { |
b55afa0c | 212 | struct blk_desc *desc; |
59da9d47 AT |
213 | const struct blk_ops *ops; |
214 | ||
b55afa0c SG |
215 | desc = dev_get_blk(dev); |
216 | if (!desc) | |
59da9d47 AT |
217 | return -ENOSYS; |
218 | ||
219 | ops = blk_get_ops(dev); | |
220 | if (!ops->write) | |
221 | return -ENOSYS; | |
222 | ||
b55afa0c | 223 | blkcache_invalidate(desc->uclass_id, desc->devnum); |
59da9d47 AT |
224 | |
225 | return ops->write(dev, start, blkcnt, buffer); | |
226 | } | |
227 | ||
76c839fc SG |
228 | unsigned long disk_blk_erase(struct udevice *dev, lbaint_t start, |
229 | lbaint_t blkcnt) | |
59da9d47 | 230 | { |
b55afa0c | 231 | struct blk_desc *desc; |
59da9d47 AT |
232 | const struct blk_ops *ops; |
233 | ||
b55afa0c SG |
234 | desc = dev_get_blk(dev); |
235 | if (!desc) | |
59da9d47 AT |
236 | return -ENOSYS; |
237 | ||
238 | ops = blk_get_ops(dev); | |
239 | if (!ops->erase) | |
240 | return -ENOSYS; | |
241 | ||
b55afa0c | 242 | blkcache_invalidate(desc->uclass_id, desc->devnum); |
59da9d47 AT |
243 | |
244 | return ops->erase(dev, start, blkcnt); | |
245 | } | |
246 | ||
43855fdb AT |
247 | UCLASS_DRIVER(partition) = { |
248 | .id = UCLASS_PARTITION, | |
249 | .per_device_plat_auto = sizeof(struct disk_part), | |
250 | .name = "partition", | |
251 | }; |