diff options
Diffstat (limited to 'drivers/of/partition.c')
-rw-r--r-- | drivers/of/partition.c | 144 |
1 files changed, 98 insertions, 46 deletions
diff --git a/drivers/of/partition.c b/drivers/of/partition.c index 655b67f854..df66751fe9 100644 --- a/drivers/of/partition.c +++ b/drivers/of/partition.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * partition.c - devicetree partition parsing * * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix * * based on Linux devicetree support - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> #include <of.h> @@ -23,6 +12,7 @@ #include <linux/mtd/mtd.h> #include <linux/err.h> #include <nand.h> +#include <linux/nvmem-provider.h> #include <init.h> #include <globalvar.h> @@ -36,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) @@ -57,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) @@ -70,22 +58,31 @@ 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; + } + + new->device_node = node; + new->flags |= DEVFS_PARTITION_FROM_OF | DEVFS_PARTITION_FOR_FIXUP; - if (new) - new->device_node = node;; + if (IS_ENABLED(CONFIG_NVMEM) && of_device_is_compatible(node, "nvmem-cells")) { + struct nvmem_device *nvmem = nvmem_partition_register(new); + if (IS_ERR(nvmem)) + dev_warn(cdev->dev, "nvmem registeration failed: %pe\n", nvmem); + } +out: free(filename); return new; @@ -98,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) { @@ -114,6 +111,50 @@ 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) +{ + 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); + } + + /* Check if legacy partitions binding */ + if (!of_property_present(np, "compatible") || + of_device_is_compatible(np, "nvmem-cells")) + return of_device_ensure_probed(parent); + + /* Doesn't look like a partition, so let's probe directly */ + return of_device_ensure_probed(np); +} +EXPORT_SYMBOL_GPL(of_partition_ensure_probed); + static void delete_subnodes(struct device_node *np) { struct device_node *part, *tmp; @@ -126,22 +167,18 @@ static void delete_subnodes(struct device_node *np) } } -static int of_partition_fixup(struct device_node *root, void *ctx) +int of_fixup_partitions(struct device_node *np, struct cdev *cdev) { - struct cdev *cdev = ctx, *partcdev; - struct device_node *np, *part, *partnode; - char *name; + struct cdev *partcdev; + struct device_node *part, *partnode; int ret; int n_cells, n_parts = 0; if (of_partition_binding == MTD_OF_BINDING_DONTTOUCH) return 0; - if (!cdev->device_node) - return -EINVAL; - 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++; } @@ -154,15 +191,6 @@ static int of_partition_fixup(struct device_node *root, void *ctx) else n_cells = 1; - name = of_get_reproducible_name(cdev->device_node); - 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); - return -EINVAL; - } - partnode = of_get_child_by_name(np, "partitions"); if (partnode) { if (of_partition_binding == MTD_OF_BINDING_LEGACY) { @@ -201,7 +229,7 @@ static int of_partition_fixup(struct device_node *root, void *ctx) 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) @@ -245,6 +273,30 @@ static int of_partition_fixup(struct device_node *root, void *ctx) return 0; } +static int of_partition_fixup(struct device_node *root, void *ctx) +{ + struct cdev *cdev = ctx; + struct device_node *cdev_np, *np; + char *name; + + cdev_np = cdev_of_node(cdev); + if (!cdev_np) + return -EINVAL; + + if (list_empty(&cdev->partitions)) + return 0; + + 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 %pOF, cannot fixup\n", cdev_np); + return -EINVAL; + } + + return of_fixup_partitions(np, cdev); +} + int of_partitions_register_fixup(struct cdev *cdev) { return of_register_fixup(of_partition_fixup, cdev); |