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 
part_create_block_devices(struct udevice * blk_dev)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 
part_blk_read(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * buffer)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 
part_blk_write(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,const void * buffer)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 
part_blk_erase(struct udevice * dev,lbaint_t start,lbaint_t blkcnt)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  */
dev_get_blk(struct udevice * dev)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 
disk_blk_read(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * buffer)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 
disk_blk_write(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,const void * buffer)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 
disk_blk_erase(struct udevice * dev,lbaint_t start,lbaint_t blkcnt)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 };
248