summaryrefslogtreecommitdiffstats
path: root/drivers/block/blk-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/blk-uclass.c')
-rw-r--r--drivers/block/blk-uclass.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index 617db226a2..6ba1026f58 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -11,6 +11,315 @@
#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_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = blk_get_device(if_type, devnum, &dev);
+ if (ret)
+ return ret;
+
+ return blk_select_hwpart(dev, hwpart);
+}
+
+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_select_hwpart(struct udevice *dev, int hwpart)
+{
+ const struct blk_ops *ops = blk_get_ops(dev);
+
+ if (!ops)
+ return -ENOSYS;
+ if (!ops->select_hwpart)
+ return 0;
+
+ return ops->select_hwpart(dev, hwpart);
+}
+
+int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
+{
+ return blk_select_hwpart(desc->bdev, hwpart);
+}
+
int blk_first_device(int if_type, struct udevice **devp)
{
struct blk_desc *desc;
@@ -131,6 +440,26 @@ int blk_prepare_device(struct udevice *dev)
return 0;
}
+int blk_find_max_devnum(enum if_type if_type)
+{
+ struct udevice *dev;
+ int max_devnum = -ENODEV;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(dev, uc) {
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ if (desc->if_type == if_type && desc->devnum > max_devnum)
+ max_devnum = desc->devnum;
+ }
+
+ return max_devnum;
+}
+
int 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)
@@ -139,6 +468,15 @@ int blk_create_device(struct udevice *parent, const char *drv_name,
struct udevice *dev;
int ret;
+ if (devnum == -1) {
+ ret = blk_find_max_devnum(if_type);
+ if (ret == -ENODEV)
+ devnum = 0;
+ else if (ret < 0)
+ return ret;
+ else
+ devnum = ret + 1;
+ }
ret = device_bind_driver(parent, drv_name, name, &dev);
if (ret)
return ret;
@@ -154,6 +492,29 @@ int blk_create_device(struct udevice *parent, const char *drv_name,
return 0;
}
+int 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)
+{
+ char dev_name[30], *str;
+ int ret;
+
+ snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
+ str = strdup(dev_name);
+ if (!str)
+ return -ENOMEM;
+
+ ret = blk_create_device(parent, drv_name, str, if_type, devnum,
+ blksz, size, devp);
+ if (ret) {
+ free(str);
+ return ret;
+ }
+ device_set_name_alloced(*devp);
+
+ return ret;
+}
+
int blk_unbind_all(int if_type)
{
struct uclass *uc;
OpenPOWER on IntegriCloud