diff options
Diffstat (limited to 'drivers/of/partition.c')
-rw-r--r-- | drivers/of/partition.c | 88 |
1 files changed, 61 insertions, 27 deletions
diff --git a/drivers/of/partition.c b/drivers/of/partition.c index 6c05e28ea9..df66751fe9 100644 --- a/drivers/of/partition.c +++ b/drivers/of/partition.c @@ -26,14 +26,12 @@ enum of_binding_name { struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) { + struct devfs_partition partinfo = {}; const char *partname; char *filename; struct cdev *new; const __be32 *reg; - u64 offset, size; - const char *name; int len; - unsigned long flags = 0; int na, ns; if (!node) @@ -47,12 +45,12 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) ns = of_n_size_cells(node); if (len < (na + ns) * sizeof(__be32)) { - pr_err("reg property too small in %s\n", node->full_name); + pr_err("reg property too small in %pOF\n", node); return NULL; } - offset = of_read_number(reg, na); - size = of_read_number(reg + na, ns); + partinfo.offset = of_read_number(reg, na); + partinfo.size = of_read_number(reg + na, ns); partname = of_get_property(node, "label", NULL); if (!partname) @@ -60,21 +58,23 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) if (!partname) return NULL; - name = (char *)partname; - - debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, offset, size); + debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, + partinfo.offset, partinfo.size); if (of_get_property(node, "read-only", NULL)) - flags = DEVFS_PARTITION_READONLY; + partinfo.flags = DEVFS_PARTITION_READONLY; - filename = basprintf("%s.%s", cdev->name, partname); + partinfo.name = filename = basprintf("%s.%s", cdev->name, partname); - new = devfs_add_partition(cdev->name, offset, size, flags, filename); - if (IS_ERR(new)) + new = cdevfs_add_partition(cdev, &partinfo); + if (IS_ERR(new)) { + pr_err("Adding partition %s failed: %pe\n", filename, new); new = NULL; + goto out; + } - if (new) - new->device_node = node;; + new->device_node = node; + new->flags |= DEVFS_PARTITION_FROM_OF | DEVFS_PARTITION_FOR_FIXUP; if (IS_ENABLED(CONFIG_NVMEM) && of_device_is_compatible(node, "nvmem-cells")) { struct nvmem_device *nvmem = nvmem_partition_register(new); @@ -82,6 +82,7 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) dev_warn(cdev->dev, "nvmem registeration failed: %pe\n", nvmem); } +out: free(filename); return new; @@ -94,7 +95,7 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node) if (!node) return -EINVAL; - cdev->device_node = node; + cdev_set_of_node(cdev, node); subnode = of_get_child_by_name(node, "partitions"); if (subnode) { @@ -110,14 +111,47 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node) return 0; } +/** + * of_partition_ensure_probed - ensure a parition is probed + * @np: pointer to a partition or to a partitionable device + * Unfortunately, there is no completely reliable way + * to differentiate partitions from devices prior to + * probing, because partitions may also have compatibles. + * We only handle nvmem-cells, so anything besides that + * is assumed to be a device that should be probed directly. + * + * Returns zero on success or a negative error code otherwise + */ int of_partition_ensure_probed(struct device_node *np) { - np = of_get_parent(np); + struct device_node *parent = of_get_parent(np); + + /* root node is not a partition */ + if (!parent) + return -EINVAL; + + /* Check if modern partitions binding */ + if (of_device_is_compatible(parent, "fixed-partitions")) { + parent = of_get_parent(parent); + + /* + * Can't call of_partition_ensure_probed on root node. + * This catches barebox-specific partuuid binding + * (top-level partition node) + */ + if (!of_get_parent(parent)) + return -EINVAL; + + return of_device_ensure_probed(parent); + } - if (of_device_is_compatible(np, "fixed-partitions")) - np = of_get_parent(np); + /* Check if legacy partitions binding */ + if (!of_property_present(np, "compatible") || + of_device_is_compatible(np, "nvmem-cells")) + return of_device_ensure_probed(parent); - return np ? of_device_ensure_probed(np) : -EINVAL; + /* Doesn't look like a partition, so let's probe directly */ + return of_device_ensure_probed(np); } EXPORT_SYMBOL_GPL(of_partition_ensure_probed); @@ -144,7 +178,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev) return 0; list_for_each_entry(partcdev, &cdev->partitions, partition_entry) { - if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE) + if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP)) continue; n_parts++; } @@ -195,7 +229,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev) u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */ loff_t partoffset; - if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE) + if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP)) continue; if (partcdev->mtd) @@ -242,21 +276,21 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev) static int of_partition_fixup(struct device_node *root, void *ctx) { struct cdev *cdev = ctx; - struct device_node *np; + struct device_node *cdev_np, *np; char *name; - if (!cdev->device_node) + cdev_np = cdev_of_node(cdev); + if (!cdev_np) return -EINVAL; if (list_empty(&cdev->partitions)) return 0; - name = of_get_reproducible_name(cdev->device_node); + name = of_get_reproducible_name(cdev_np); np = of_find_node_by_reproducible_name(root, name); free(name); if (!np) { - dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n", - cdev->device_node->full_name); + dev_err(cdev->dev, "Cannot find nodepath %pOF, cannot fixup\n", cdev_np); return -EINVAL; } |