summaryrefslogtreecommitdiffstats
path: root/drivers/of/partition.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/partition.c')
-rw-r--r--drivers/of/partition.c144
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);