diff options
-rw-r--r-- | drivers/block/blk-uclass.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 617db226a2..3687b9a100 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -11,6 +11,286 @@ #include <dm/device-internal.h> #include <dm/lists.h> +static const char *if_typename_str[IF_TYPE_COUNT] = { + [IF_TYPE_IDE] = "ide", + [IF_TYPE_SCSI] = "scsi", + [IF_TYPE_ATAPI] = "atapi", + [IF_TYPE_USB] = "usb", + [IF_TYPE_DOC] = "doc", + [IF_TYPE_MMC] = "mmc", + [IF_TYPE_SD] = "sd", + [IF_TYPE_SATA] = "sata", + [IF_TYPE_HOST] = "host", + [IF_TYPE_SYSTEMACE] = "ace", +}; + +static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = { + [IF_TYPE_IDE] = UCLASS_INVALID, + [IF_TYPE_SCSI] = UCLASS_INVALID, + [IF_TYPE_ATAPI] = UCLASS_INVALID, + [IF_TYPE_USB] = UCLASS_MASS_STORAGE, + [IF_TYPE_DOC] = UCLASS_INVALID, + [IF_TYPE_MMC] = UCLASS_MMC, + [IF_TYPE_SD] = UCLASS_INVALID, + [IF_TYPE_SATA] = UCLASS_AHCI, + [IF_TYPE_HOST] = UCLASS_ROOT, + [IF_TYPE_SYSTEMACE] = UCLASS_INVALID, +}; + +static enum if_type if_typename_to_iftype(const char *if_typename) +{ + int i; + + for (i = 0; i < IF_TYPE_COUNT; i++) { + if (if_typename_str[i] && + !strcmp(if_typename, if_typename_str[i])) + return i; + } + + return IF_TYPE_UNKNOWN; +} + +static enum uclass_id if_type_to_uclass_id(enum if_type if_type) +{ + return if_type_uclass_id[if_type]; +} + +struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum) +{ + struct blk_desc *desc; + struct udevice *dev; + int ret; + + ret = blk_get_device(if_type, devnum, &dev); + if (ret) + return NULL; + desc = dev_get_uclass_platdata(dev); + + return desc; +} + +/* + * This function is complicated with driver model. We look up the interface + * name in a local table. This gives us an interface type which we can match + * against the uclass of the block device's parent. + */ +struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum) +{ + enum uclass_id uclass_id; + enum if_type if_type; + struct udevice *dev; + struct uclass *uc; + int ret; + + if_type = if_typename_to_iftype(if_typename); + if (if_type == IF_TYPE_UNKNOWN) { + debug("%s: Unknown interface type '%s'\n", __func__, + if_typename); + return NULL; + } + uclass_id = if_type_to_uclass_id(if_type); + if (uclass_id == UCLASS_INVALID) { + debug("%s: Unknown uclass for interface type'\n", + if_typename_str[if_type]); + return NULL; + } + + ret = uclass_get(UCLASS_BLK, &uc); + if (ret) + return NULL; + uclass_foreach_dev(dev, uc) { + struct blk_desc *desc = dev_get_uclass_platdata(dev); + + debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__, + if_type, devnum, dev->name, desc->if_type, desc->devnum); + if (desc->devnum != devnum) + continue; + + /* Find out the parent device uclass */ + if (device_get_uclass_id(dev->parent) != uclass_id) { + debug("%s: parent uclass %d, this dev %d\n", __func__, + device_get_uclass_id(dev->parent), uclass_id); + continue; + } + + if (device_probe(dev)) + return NULL; + + debug("%s: Device desc %p\n", __func__, desc); + return desc; + } + debug("%s: No device found\n", __func__); + + return NULL; +} + +/** + * get_desc() - Get the block device descriptor for the given device number + * + * @if_type: Interface type + * @devnum: Device number (0 = first) + * @descp: Returns block device descriptor on success + * @return 0 on success, -ENODEV if there is no such device and no device + * with a higher device number, -ENOENT if there is no such device but there + * is one with a higher number, or other -ve on other error. + */ +static int get_desc(enum if_type if_type, int devnum, struct blk_desc **descp) +{ + bool found_more = false; + struct udevice *dev; + struct uclass *uc; + int ret; + + *descp = NULL; + ret = uclass_get(UCLASS_BLK, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct blk_desc *desc = dev_get_uclass_platdata(dev); + + debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__, + if_type, devnum, dev->name, desc->if_type, desc->devnum); + if (desc->if_type == if_type) { + if (desc->devnum == devnum) { + ret = device_probe(dev); + if (ret) + return ret; + + } else if (desc->devnum > devnum) { + found_more = true; + } + } + } + + return found_more ? -ENOENT : -ENODEV; +} + +int blk_list_part(enum if_type if_type) +{ + struct blk_desc *desc; + int devnum, ok; + int ret; + + for (ok = 0, devnum = 0;; ++devnum) { + ret = get_desc(if_type, devnum, &desc); + if (ret == -ENODEV) + break; + else if (ret) + continue; + if (desc->part_type != PART_TYPE_UNKNOWN) { + ++ok; + if (devnum) + putc('\n'); + part_print(desc); + } + } + if (!ok) + return -ENODEV; + + return 0; +} + +int blk_print_part_devnum(enum if_type if_type, int devnum) +{ + struct blk_desc *desc; + int ret; + + ret = get_desc(if_type, devnum, &desc); + if (ret) + return ret; + if (desc->type == DEV_TYPE_UNKNOWN) + return -ENOENT; + part_print(desc); + + return 0; +} + +void blk_list_devices(enum if_type if_type) +{ + struct blk_desc *desc; + int ret; + int i; + + for (i = 0;; ++i) { + ret = get_desc(if_type, i, &desc); + if (ret == -ENODEV) + break; + else if (ret) + continue; + if (desc->type == DEV_TYPE_UNKNOWN) + continue; /* list only known devices */ + printf("Device %d: ", i); + dev_print(desc); + } +} + +int blk_print_device_num(enum if_type if_type, int devnum) +{ + struct blk_desc *desc; + int ret; + + ret = get_desc(if_type, devnum, &desc); + if (ret) + return ret; + printf("\nIDE device %d: ", devnum); + dev_print(desc); + + return 0; +} + +int blk_show_device(enum if_type if_type, int devnum) +{ + struct blk_desc *desc; + int ret; + + printf("\nDevice %d: ", devnum); + ret = get_desc(if_type, devnum, &desc); + if (ret == -ENODEV || ret == -ENOENT) { + printf("unknown device\n"); + return -ENODEV; + } + if (ret) + return ret; + dev_print(desc); + + if (desc->type == DEV_TYPE_UNKNOWN) + return -ENOENT; + + return 0; +} + +ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start, + lbaint_t blkcnt, void *buffer) +{ + struct blk_desc *desc; + ulong n; + int ret; + + ret = get_desc(if_type, devnum, &desc); + if (ret) + return ret; + n = blk_dread(desc, start, blkcnt, buffer); + if (IS_ERR_VALUE(n)) + return n; + + /* flush cache after read */ + flush_cache((ulong)buffer, blkcnt * desc->blksz); + + return n; +} + +ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start, + lbaint_t blkcnt, const void *buffer) +{ + struct blk_desc *desc; + int ret; + + ret = get_desc(if_type, devnum, &desc); + if (ret) + return ret; + return blk_dwrite(desc, start, blkcnt, buffer); +} + int blk_first_device(int if_type, struct udevice **devp) { struct blk_desc *desc; |