1 /*
2 * Copyright (C) 2016 Google, Inc
3 * Written by Simon Glass <sjg@chromium.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
8 #include <common.h>
9 #include <blk.h>
10 #include <dm.h>
11 #include <dm/device-internal.h>
12 #include <dm/lists.h>
13 #include <dm/uclass-internal.h>
14
15 static const char *if_typename_str[IF_TYPE_COUNT] = {
16 [IF_TYPE_IDE] = "ide",
17 [IF_TYPE_SCSI] = "scsi",
18 [IF_TYPE_ATAPI] = "atapi",
19 [IF_TYPE_USB] = "usb",
20 [IF_TYPE_DOC] = "doc",
21 [IF_TYPE_MMC] = "mmc",
22 [IF_TYPE_SD] = "sd",
23 [IF_TYPE_SATA] = "sata",
24 [IF_TYPE_HOST] = "host",
25 [IF_TYPE_SYSTEMACE] = "ace",
26 [IF_TYPE_NVME] = "nvme",
27 [IF_TYPE_RKNAND] = "rknand",
28 [IF_TYPE_SPINAND] = "spinand",
29 [IF_TYPE_SPINOR] = "spinor",
30 [IF_TYPE_RAMDISK] = "ramdisk",
31 [IF_TYPE_MTD] = "mtd",
32 };
33
34 static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
35 [IF_TYPE_IDE] = UCLASS_IDE,
36 [IF_TYPE_SCSI] = UCLASS_SCSI,
37 [IF_TYPE_ATAPI] = UCLASS_INVALID,
38 [IF_TYPE_USB] = UCLASS_MASS_STORAGE,
39 [IF_TYPE_DOC] = UCLASS_INVALID,
40 [IF_TYPE_MMC] = UCLASS_MMC,
41 [IF_TYPE_SD] = UCLASS_INVALID,
42 [IF_TYPE_SATA] = UCLASS_AHCI,
43 [IF_TYPE_HOST] = UCLASS_ROOT,
44 [IF_TYPE_NVME] = UCLASS_NVME,
45 [IF_TYPE_RKNAND] = UCLASS_RKNAND,
46 [IF_TYPE_SPINAND] = UCLASS_SPI_FLASH,
47 [IF_TYPE_SPINOR] = UCLASS_SPI_FLASH,
48 [IF_TYPE_RAMDISK] = UCLASS_RAMDISK,
49 [IF_TYPE_MTD] = UCLASS_MTD,
50 [IF_TYPE_SYSTEMACE] = UCLASS_INVALID,
51 };
52
if_typename_to_iftype(const char * if_typename)53 enum if_type if_typename_to_iftype(const char *if_typename)
54 {
55 int i;
56
57 for (i = 0; i < IF_TYPE_COUNT; i++) {
58 if (if_typename_str[i] &&
59 !strcmp(if_typename, if_typename_str[i]))
60 return i;
61 }
62
63 return IF_TYPE_UNKNOWN;
64 }
65
if_type_to_uclass_id(enum if_type if_type)66 static enum uclass_id if_type_to_uclass_id(enum if_type if_type)
67 {
68 return if_type_uclass_id[if_type];
69 }
70
blk_get_if_type_name(enum if_type if_type)71 const char *blk_get_if_type_name(enum if_type if_type)
72 {
73 return if_typename_str[if_type];
74 }
75
blk_get_devnum_by_type(enum if_type if_type,int devnum)76 struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
77 {
78 struct blk_desc *desc;
79 struct udevice *dev;
80 int ret;
81
82 ret = blk_get_device(if_type, devnum, &dev);
83 if (ret)
84 return NULL;
85 desc = dev_get_uclass_platdata(dev);
86
87 return desc;
88 }
89
90 /*
91 * This function is complicated with driver model. We look up the interface
92 * name in a local table. This gives us an interface type which we can match
93 * against the uclass of the block device's parent.
94 */
blk_get_devnum_by_typename(const char * if_typename,int devnum)95 struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
96 {
97 enum uclass_id uclass_id;
98 enum if_type if_type;
99 struct udevice *dev;
100 struct uclass *uc;
101 int ret;
102
103 if_type = if_typename_to_iftype(if_typename);
104 if (if_type == IF_TYPE_UNKNOWN) {
105 debug("%s: Unknown interface type '%s'\n", __func__,
106 if_typename);
107 return NULL;
108 }
109 uclass_id = if_type_to_uclass_id(if_type);
110 if (uclass_id == UCLASS_INVALID) {
111 debug("%s: Unknown uclass for interface type'\n",
112 if_typename_str[if_type]);
113 return NULL;
114 }
115
116 ret = uclass_get(UCLASS_BLK, &uc);
117 if (ret)
118 return NULL;
119 uclass_foreach_dev(dev, uc) {
120 struct blk_desc *desc = dev_get_uclass_platdata(dev);
121
122 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
123 if_type, devnum, dev->name, desc->if_type, desc->devnum);
124 if (desc->devnum != devnum)
125 continue;
126
127 /* Find out the parent device uclass */
128 if (device_get_uclass_id(dev->parent) != uclass_id) {
129 #ifdef CONFIG_MTD_BLK
130 /*
131 * The normal mtd block attachment steps are
132 * UCLASS_BLK -> UCLASS_MTD -> UCLASS_(SPI or NAND).
133 * Since the spi flash frame is attached to
134 * UCLASS_SPI_FLASH, this make mistake to find
135 * the UCLASS_MTD when find the mtd block device.
136 * Fix it here when enable CONFIG_MTD_BLK.
137 */
138 if (device_get_uclass_id(dev->parent) == UCLASS_SPI_FLASH &&
139 if_type == IF_TYPE_MTD &&
140 devnum == BLK_MTD_SPI_NOR) {
141 debug("Fix the spi flash uclass different\n");
142 } else {
143 debug("%s: parent uclass %d, this dev %d\n",
144 __func__,
145 device_get_uclass_id(dev->parent),
146 uclass_id);
147 continue;
148 }
149 #else
150 debug("%s: parent uclass %d, this dev %d\n", __func__,
151 device_get_uclass_id(dev->parent), uclass_id);
152 continue;
153 #endif
154 }
155
156 if (device_probe(dev))
157 return NULL;
158
159 debug("%s: Device desc %p\n", __func__, desc);
160 return desc;
161 }
162 debug("%s: No device found\n", __func__);
163
164 return NULL;
165 }
166
167 /**
168 * get_desc() - Get the block device descriptor for the given device number
169 *
170 * @if_type: Interface type
171 * @devnum: Device number (0 = first)
172 * @descp: Returns block device descriptor on success
173 * @return 0 on success, -ENODEV if there is no such device and no device
174 * with a higher device number, -ENOENT if there is no such device but there
175 * is one with a higher number, or other -ve on other error.
176 */
get_desc(enum if_type if_type,int devnum,struct blk_desc ** descp)177 static int get_desc(enum if_type if_type, int devnum, struct blk_desc **descp)
178 {
179 bool found_more = false;
180 struct udevice *dev;
181 struct uclass *uc;
182 int ret;
183
184 *descp = NULL;
185 ret = uclass_get(UCLASS_BLK, &uc);
186 if (ret)
187 return ret;
188 uclass_foreach_dev(dev, uc) {
189 struct blk_desc *desc = dev_get_uclass_platdata(dev);
190
191 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
192 if_type, devnum, dev->name, desc->if_type, desc->devnum);
193 if (desc->if_type == if_type) {
194 if (desc->devnum == devnum) {
195 ret = device_probe(dev);
196 if (ret)
197 return ret;
198
199 *descp = desc;
200 return 0;
201 } else if (desc->devnum > devnum) {
202 found_more = true;
203 }
204 }
205 }
206
207 return found_more ? -ENOENT : -ENODEV;
208 }
209
blk_select_hwpart_devnum(enum if_type if_type,int devnum,int hwpart)210 int blk_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
211 {
212 struct udevice *dev;
213 int ret;
214
215 ret = blk_get_device(if_type, devnum, &dev);
216 if (ret)
217 return ret;
218
219 return blk_select_hwpart(dev, hwpart);
220 }
221
blk_list_part(enum if_type if_type)222 int blk_list_part(enum if_type if_type)
223 {
224 struct blk_desc *desc;
225 int devnum, ok;
226 int ret;
227
228 for (ok = 0, devnum = 0;; ++devnum) {
229 ret = get_desc(if_type, devnum, &desc);
230 if (ret == -ENODEV)
231 break;
232 else if (ret)
233 continue;
234 if (desc->part_type != PART_TYPE_UNKNOWN) {
235 ++ok;
236 if (devnum)
237 putc('\n');
238 part_print(desc);
239 }
240 }
241 if (!ok)
242 return -ENODEV;
243
244 return 0;
245 }
246
blk_print_part_devnum(enum if_type if_type,int devnum)247 int blk_print_part_devnum(enum if_type if_type, int devnum)
248 {
249 struct blk_desc *desc;
250 int ret;
251
252 ret = get_desc(if_type, devnum, &desc);
253 if (ret)
254 return ret;
255 if (desc->type == DEV_TYPE_UNKNOWN)
256 return -ENOENT;
257 part_print(desc);
258
259 return 0;
260 }
261
blk_list_devices(enum if_type if_type)262 void blk_list_devices(enum if_type if_type)
263 {
264 struct blk_desc *desc;
265 int ret;
266 int i;
267
268 for (i = 0;; ++i) {
269 ret = get_desc(if_type, i, &desc);
270 if (ret == -ENODEV)
271 break;
272 else if (ret)
273 continue;
274 if (desc->type == DEV_TYPE_UNKNOWN)
275 continue; /* list only known devices */
276 printf("Device %d: ", i);
277 dev_print(desc);
278 }
279 }
280
blk_print_device_num(enum if_type if_type,int devnum)281 int blk_print_device_num(enum if_type if_type, int devnum)
282 {
283 struct blk_desc *desc;
284 int ret;
285
286 ret = get_desc(if_type, devnum, &desc);
287 if (ret)
288 return ret;
289 printf("\nIDE device %d: ", devnum);
290 dev_print(desc);
291
292 return 0;
293 }
294
blk_show_device(enum if_type if_type,int devnum)295 int blk_show_device(enum if_type if_type, int devnum)
296 {
297 struct blk_desc *desc;
298 int ret;
299
300 printf("\nDevice %d: ", devnum);
301 ret = get_desc(if_type, devnum, &desc);
302 if (ret == -ENODEV || ret == -ENOENT) {
303 printf("unknown device\n");
304 return -ENODEV;
305 }
306 if (ret)
307 return ret;
308 dev_print(desc);
309
310 if (desc->type == DEV_TYPE_UNKNOWN)
311 return -ENOENT;
312
313 return 0;
314 }
315
blk_read_devnum(enum if_type if_type,int devnum,lbaint_t start,lbaint_t blkcnt,void * buffer)316 ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
317 lbaint_t blkcnt, void *buffer)
318 {
319 struct blk_desc *desc;
320 ulong n;
321 int ret;
322
323 ret = get_desc(if_type, devnum, &desc);
324 if (ret)
325 return ret;
326 n = blk_dread(desc, start, blkcnt, buffer);
327 if (IS_ERR_VALUE(n))
328 return n;
329
330 return n;
331 }
332
blk_write_devnum(enum if_type if_type,int devnum,lbaint_t start,lbaint_t blkcnt,const void * buffer)333 ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
334 lbaint_t blkcnt, const void *buffer)
335 {
336 struct blk_desc *desc;
337 int ret;
338
339 ret = get_desc(if_type, devnum, &desc);
340 if (ret)
341 return ret;
342 return blk_dwrite(desc, start, blkcnt, buffer);
343 }
344
blk_write_zeroes_devnum(enum if_type if_type,int devnum,lbaint_t start,lbaint_t blkcnt)345 ulong blk_write_zeroes_devnum(enum if_type if_type, int devnum, lbaint_t start,
346 lbaint_t blkcnt)
347 {
348 struct blk_desc *desc;
349 int ret;
350
351 ret = get_desc(if_type, devnum, &desc);
352 if (ret)
353 return ret;
354 return blk_dwrite_zeroes(desc, start, blkcnt);
355 }
356
blk_erase_devnum(enum if_type if_type,int devnum,lbaint_t start,lbaint_t blkcnt)357 ulong blk_erase_devnum(enum if_type if_type, int devnum, lbaint_t start,
358 lbaint_t blkcnt)
359 {
360 struct blk_desc *desc;
361 int ret;
362
363 ret = get_desc(if_type, devnum, &desc);
364 if (ret)
365 return ret;
366 return blk_derase(desc, start, blkcnt);
367 }
368
blk_select_hwpart(struct udevice * dev,int hwpart)369 int blk_select_hwpart(struct udevice *dev, int hwpart)
370 {
371 const struct blk_ops *ops = blk_get_ops(dev);
372
373 if (!ops)
374 return -ENOSYS;
375 if (!ops->select_hwpart)
376 return 0;
377
378 return ops->select_hwpart(dev, hwpart);
379 }
380
blk_dselect_hwpart(struct blk_desc * desc,int hwpart)381 int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
382 {
383 return blk_select_hwpart(desc->bdev, hwpart);
384 }
385
blk_first_device(int if_type,struct udevice ** devp)386 int blk_first_device(int if_type, struct udevice **devp)
387 {
388 struct blk_desc *desc;
389 int ret;
390
391 ret = uclass_find_first_device(UCLASS_BLK, devp);
392 if (ret)
393 return ret;
394 if (!*devp)
395 return -ENODEV;
396 do {
397 desc = dev_get_uclass_platdata(*devp);
398 if (desc->if_type == if_type)
399 return 0;
400 ret = uclass_find_next_device(devp);
401 if (ret)
402 return ret;
403 } while (*devp);
404
405 return -ENODEV;
406 }
407
blk_next_device(struct udevice ** devp)408 int blk_next_device(struct udevice **devp)
409 {
410 struct blk_desc *desc;
411 int ret, if_type;
412
413 desc = dev_get_uclass_platdata(*devp);
414 if_type = desc->if_type;
415 do {
416 ret = uclass_find_next_device(devp);
417 if (ret)
418 return ret;
419 if (!*devp)
420 return -ENODEV;
421 desc = dev_get_uclass_platdata(*devp);
422 if (desc->if_type == if_type)
423 return 0;
424 } while (1);
425 }
426
blk_find_device(int if_type,int devnum,struct udevice ** devp)427 int blk_find_device(int if_type, int devnum, struct udevice **devp)
428 {
429 struct uclass *uc;
430 struct udevice *dev;
431 int ret;
432
433 ret = uclass_get(UCLASS_BLK, &uc);
434 if (ret)
435 return ret;
436 uclass_foreach_dev(dev, uc) {
437 struct blk_desc *desc = dev_get_uclass_platdata(dev);
438
439 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
440 if_type, devnum, dev->name, desc->if_type, desc->devnum);
441 if (desc->if_type == if_type && desc->devnum == devnum) {
442 *devp = dev;
443 return 0;
444 }
445 }
446
447 return -ENODEV;
448 }
449
blk_get_device(int if_type,int devnum,struct udevice ** devp)450 int blk_get_device(int if_type, int devnum, struct udevice **devp)
451 {
452 int ret;
453
454 ret = blk_find_device(if_type, devnum, devp);
455 if (ret)
456 return ret;
457
458 return device_probe(*devp);
459 }
460
blk_dread(struct blk_desc * block_dev,lbaint_t start,lbaint_t blkcnt,void * buffer)461 unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
462 lbaint_t blkcnt, void *buffer)
463 {
464 struct udevice *dev = block_dev->bdev;
465 const struct blk_ops *ops = blk_get_ops(dev);
466 ulong blks_read;
467
468 if (!ops->read)
469 return -ENOSYS;
470
471 if (blkcache_read(block_dev->if_type, block_dev->devnum,
472 start, blkcnt, block_dev->blksz, buffer))
473 return blkcnt;
474
475 u_spin_lock(&block_dev->blk_lock);
476 blks_read = ops->read(dev, start, blkcnt, buffer);
477 u_spin_unlock(&block_dev->blk_lock);
478
479 if (blks_read == blkcnt)
480 blkcache_fill(block_dev->if_type, block_dev->devnum,
481 start, blkcnt, block_dev->blksz, buffer);
482
483 return blks_read;
484 }
485
blk_dwrite(struct blk_desc * block_dev,lbaint_t start,lbaint_t blkcnt,const void * buffer)486 unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
487 lbaint_t blkcnt, const void *buffer)
488 {
489 struct udevice *dev = block_dev->bdev;
490 const struct blk_ops *ops = blk_get_ops(dev);
491 ulong ret;
492
493 if (!ops->write)
494 return -ENOSYS;
495
496 blkcache_invalidate(block_dev->if_type, block_dev->devnum);
497
498 u_spin_lock(&block_dev->blk_lock);
499 ret = ops->write(dev, start, blkcnt, buffer);
500 u_spin_unlock(&block_dev->blk_lock);
501
502 return ret;
503 }
504
blk_dwrite_zeroes(struct blk_desc * block_dev,lbaint_t start,lbaint_t blkcnt)505 unsigned long blk_dwrite_zeroes(struct blk_desc *block_dev, lbaint_t start,
506 lbaint_t blkcnt)
507 {
508 struct udevice *dev = block_dev->bdev;
509 const struct blk_ops *ops = blk_get_ops(dev);
510 ulong ret;
511
512 if (!ops->write_zeroes)
513 return -ENOSYS;
514
515 blkcache_invalidate(block_dev->if_type, block_dev->devnum);
516
517 u_spin_lock(&block_dev->blk_lock);
518 ret = ops->write_zeroes(dev, start, blkcnt);
519 u_spin_unlock(&block_dev->blk_lock);
520
521 return ret;
522 }
523
blk_derase(struct blk_desc * block_dev,lbaint_t start,lbaint_t blkcnt)524 unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
525 lbaint_t blkcnt)
526 {
527 struct udevice *dev = block_dev->bdev;
528 const struct blk_ops *ops = blk_get_ops(dev);
529 ulong ret;
530
531 if (!ops->erase)
532 return -ENOSYS;
533
534 blkcache_invalidate(block_dev->if_type, block_dev->devnum);
535
536 u_spin_lock(&block_dev->blk_lock);
537 ret = ops->erase(dev, start, blkcnt);
538 u_spin_unlock(&block_dev->blk_lock);
539
540 return ret;
541 }
542
blk_prepare_device(struct udevice * dev)543 int blk_prepare_device(struct udevice *dev)
544 {
545 struct blk_desc *desc = dev_get_uclass_platdata(dev);
546
547 part_init(desc);
548
549 return 0;
550 }
551
blk_get_from_parent(struct udevice * parent,struct udevice ** devp)552 int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
553 {
554 struct udevice *dev;
555 enum uclass_id id;
556 int ret;
557
558 device_find_first_child(parent, &dev);
559 if (!dev) {
560 debug("%s: No block device found for parent '%s'\n", __func__,
561 parent->name);
562 return -ENODEV;
563 }
564 id = device_get_uclass_id(dev);
565 if (id != UCLASS_BLK) {
566 debug("%s: Incorrect uclass %s for block device '%s'\n",
567 __func__, uclass_get_name(id), dev->name);
568 return -ENOTBLK;
569 }
570 ret = device_probe(dev);
571 if (ret)
572 return ret;
573 *devp = dev;
574
575 return 0;
576 }
577
blk_find_max_devnum(enum if_type if_type)578 int blk_find_max_devnum(enum if_type if_type)
579 {
580 struct udevice *dev;
581 int max_devnum = -ENODEV;
582 struct uclass *uc;
583 int ret;
584
585 ret = uclass_get(UCLASS_BLK, &uc);
586 if (ret)
587 return ret;
588 uclass_foreach_dev(dev, uc) {
589 struct blk_desc *desc = dev_get_uclass_platdata(dev);
590
591 if (desc->if_type == if_type && desc->devnum > max_devnum)
592 max_devnum = desc->devnum;
593 }
594
595 return max_devnum;
596 }
597
blk_next_free_devnum(enum if_type if_type)598 static int blk_next_free_devnum(enum if_type if_type)
599 {
600 int ret;
601
602 ret = blk_find_max_devnum(if_type);
603 if (ret == -ENODEV)
604 return 0;
605 if (ret < 0)
606 return ret;
607
608 return ret + 1;
609 }
610
blk_claim_devnum(enum if_type if_type,int devnum)611 static int blk_claim_devnum(enum if_type if_type, int devnum)
612 {
613 struct udevice *dev;
614 struct uclass *uc;
615 int ret;
616
617 ret = uclass_get(UCLASS_BLK, &uc);
618 if (ret)
619 return ret;
620 uclass_foreach_dev(dev, uc) {
621 struct blk_desc *desc = dev_get_uclass_platdata(dev);
622
623 if (desc->if_type == if_type && desc->devnum == devnum) {
624 int next = blk_next_free_devnum(if_type);
625
626 if (next < 0)
627 return next;
628 #ifdef CONFIG_USING_KERNEL_DTB_V2
629 /*
630 * Not allow devnum to be forced distributed.
631 * See commit (e48eeb9ea3 dm: blk: Improve block device claiming).
632 *
633 * fix like: "Device 'dwmmc@fe2b0000': seq 0 is in use by 'sdhci@fe310000'"
634 */
635 if (!(gd->flags & GD_FLG_KDTB_READY))
636 #endif
637 desc->devnum = next;
638
639 return 0;
640 }
641 }
642
643 return -ENOENT;
644 }
645
blk_create_device(struct udevice * parent,const char * drv_name,const char * name,int if_type,int devnum,int blksz,lbaint_t size,struct udevice ** devp)646 int blk_create_device(struct udevice *parent, const char *drv_name,
647 const char *name, int if_type, int devnum, int blksz,
648 lbaint_t size, struct udevice **devp)
649 {
650 struct blk_desc *desc;
651 struct udevice *dev;
652 int ret;
653
654 if (devnum == -1) {
655 devnum = blk_next_free_devnum(if_type);
656 } else {
657 ret = blk_claim_devnum(if_type, devnum);
658 if (ret < 0 && ret != -ENOENT)
659 return ret;
660 }
661 if (devnum < 0)
662 return devnum;
663 ret = device_bind_driver(parent, drv_name, name, &dev);
664 if (ret)
665 return ret;
666 desc = dev_get_uclass_platdata(dev);
667 desc->if_type = if_type;
668 desc->blksz = blksz;
669 desc->rawblksz = blksz;
670 desc->lba = size / blksz;
671 desc->rawlba = size / blksz;
672 desc->part_type = PART_TYPE_UNKNOWN;
673 desc->bdev = dev;
674 desc->devnum = devnum;
675 *devp = dev;
676
677 return 0;
678 }
679
blk_create_devicef(struct udevice * parent,const char * drv_name,const char * name,int if_type,int devnum,int blksz,lbaint_t size,struct udevice ** devp)680 int blk_create_devicef(struct udevice *parent, const char *drv_name,
681 const char *name, int if_type, int devnum, int blksz,
682 lbaint_t size, struct udevice **devp)
683 {
684 char dev_name[30], *str;
685 int ret;
686
687 snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
688 str = strdup(dev_name);
689 if (!str)
690 return -ENOMEM;
691
692 ret = blk_create_device(parent, drv_name, str, if_type, devnum,
693 blksz, size, devp);
694 if (ret) {
695 free(str);
696 return ret;
697 }
698 device_set_name_alloced(*devp);
699
700 return 0;
701 }
702
blk_unbind_all(int if_type)703 int blk_unbind_all(int if_type)
704 {
705 struct uclass *uc;
706 struct udevice *dev, *next;
707 int ret;
708
709 ret = uclass_get(UCLASS_BLK, &uc);
710 if (ret)
711 return ret;
712 uclass_foreach_dev_safe(dev, next, uc) {
713 struct blk_desc *desc = dev_get_uclass_platdata(dev);
714
715 if (desc->if_type == if_type) {
716 ret = device_remove(dev, DM_REMOVE_NORMAL);
717 if (ret)
718 return ret;
719 ret = device_unbind(dev);
720 if (ret)
721 return ret;
722 }
723 }
724
725 return 0;
726 }
727
728 UCLASS_DRIVER(blk) = {
729 .id = UCLASS_BLK,
730 .name = "blk",
731 .per_device_platdata_auto_alloc_size = sizeof(struct blk_desc),
732 };
733