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