diff mbox series

[v7,01/12] mtd: core: always create master device

Message ID 20250326152623.3897204-2-alexander.usyskin@intel.com (mailing list archive)
State New
Headers show
Series mtd: add driver for Intel discrete graphics | expand

Commit Message

Usyskin, Alexander March 26, 2025, 3:26 p.m. UTC
Create master device without partition when
CONFIG_MTD_PARTITIONED_MASTER flag is unset.

This streamlines device tree and allows to anchor
runtime power management on master device in all cases.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/mtd/mtdcore.c | 141 +++++++++++++++++++++++++++++-------------
 drivers/mtd/mtdcore.h |   2 +-
 drivers/mtd/mtdpart.c |  17 ++---
 3 files changed, 110 insertions(+), 50 deletions(-)

Comments

Miquel Raynal April 1, 2025, 3 p.m. UTC | #1
Hello Alexander,

On 26/03/2025 at 17:26:12 +02, Alexander Usyskin <alexander.usyskin@intel.com> wrote:

> Create master device without partition when
> CONFIG_MTD_PARTITIONED_MASTER flag is unset.
>
> This streamlines device tree and allows to anchor
> runtime power management on master device in all cases.
>
> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>

I successfully tested it, this patch will be applied at -rc1 to the
mtd/next tree.

I was wondering, do you think it would be possible to keep the creation
of mtd_master and make it a symbolic link to mtdx when
MTD_PARTITIONED_MASTER=y or when there is no partition? In short, always
having mtd_masterx, because I fear at some point we'll have problems
otherwise :-)

Right now we have:

- Without partition:
mtd0 (mtd0ro mtdblock0)
- With partition and MTD_PARTITIONED_MASTER=n
mtd0 (mtd0ro mtdblock0)
mtd_master0
- With partition and MTD_PARTITIONED_MASTER=y
mtd0 (mtd0ro mtdblock0) [this is the master]
mtd1 (mtd1ro mtdblock1)

I am suggesting:
- Without partition:
mtd0 (mtd0ro mtdblock0)
mtd_master0 -> link to mtd0
- With partition and MTD_PARTITIONED_MASTER=n
mtd0 (mtd0ro mtdblock0)
mtd_master0
- With partition and MTD_PARTITIONED_MASTER=y
mtd0 (mtd0ro mtdblock0) [this is the master]
mtd1 (mtd1ro mtdblock1)
mtd_master0 -> link to mtd0

What do you think? Or maybe even always mtd_master in the first place.

Richard, your point of view on this?

Thanks,
Miquèl
Usyskin, Alexander April 7, 2025, 12:47 p.m. UTC | #2
> On 26/03/2025 at 17:26:12 +02, Alexander Usyskin
> <alexander.usyskin@intel.com> wrote:
> 
> > Create master device without partition when
> > CONFIG_MTD_PARTITIONED_MASTER flag is unset.
> >
> > This streamlines device tree and allows to anchor
> > runtime power management on master device in all cases.
> >
> > Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> 
> I successfully tested it, this patch will be applied at -rc1 to the
> mtd/next tree.
> 
> I was wondering, do you think it would be possible to keep the creation
> of mtd_master and make it a symbolic link to mtdx when
> MTD_PARTITIONED_MASTER=y or when there is no partition? In short, always
> having mtd_masterx, because I fear at some point we'll have problems
> otherwise :-)
> 
> Right now we have:
> 
> - Without partition:
> mtd0 (mtd0ro mtdblock0)
> - With partition and MTD_PARTITIONED_MASTER=n
> mtd0 (mtd0ro mtdblock0)
> mtd_master0
> - With partition and MTD_PARTITIONED_MASTER=y
> mtd0 (mtd0ro mtdblock0) [this is the master]
> mtd1 (mtd1ro mtdblock1)
> 
> I am suggesting:
> - Without partition:
> mtd0 (mtd0ro mtdblock0)
> mtd_master0 -> link to mtd0
> - With partition and MTD_PARTITIONED_MASTER=n
> mtd0 (mtd0ro mtdblock0)
> mtd_master0
> - With partition and MTD_PARTITIONED_MASTER=y
> mtd0 (mtd0ro mtdblock0) [this is the master]
> mtd1 (mtd1ro mtdblock1)
> mtd_master0 -> link to mtd0
> 
> What do you think? Or maybe even always mtd_master in the first place.
> 
> Richard, your point of view on this?
> 
> Thanks,
> Miquèl

The mtd_master is completely different class to avoid mtd tree disturbances.
It is real kernel device object, I'm not sure how we can do 'link to' magic here.

About MTD_PARTITIONED_MASTER - we can treat it as another partition and 
create master device plus whole device partition as it's child with all other
partitions as children of master device.
For unpartitioned device this mean that we create master device and partition
regardless of MTD_PARTITIONED_MASTER flag.

We also can always add master device and add whole device partition as it's child,
while adding real partition as children of this whole device.
This will create three layers of devices and sounds too complicated.

- - 
Thanks,
Sasha
Miquel Raynal April 9, 2025, 9:59 a.m. UTC | #3
Hello,

> The mtd_master is completely different class to avoid mtd tree disturbances.
> It is real kernel device object, I'm not sure how we can do 'link to'
> magic here.

Maybe we can add that later if someone needs.

> About MTD_PARTITIONED_MASTER - we can treat it as another partition and 
> create master device plus whole device partition as it's child with all other
> partitions as children of master device.
> For unpartitioned device this mean that we create master device and partition
> regardless of MTD_PARTITIONED_MASTER flag.

I am not sure I follow you. I am proposing to create the mtd_master
device in all cases. I believe this is the future-proof approach. Can
you make this change?

Regarding the hierarchy, I indeed agree with what you propose:
mtd_master parent of whole partition device (if any) parent of
partitions.

Thanks,
Miquèl
Usyskin, Alexander April 10, 2025, 9:44 a.m. UTC | #4
Hi
 
> Hello,
> 
> > The mtd_master is completely different class to avoid mtd tree disturbances.
> > It is real kernel device object, I'm not sure how we can do 'link to'
> > magic here.
> 
> Maybe we can add that later if someone needs.
> 
> > About MTD_PARTITIONED_MASTER - we can treat it as another partition
> and
> > create master device plus whole device partition as it's child with all other
> > partitions as children of master device.
> > For unpartitioned device this mean that we create master device and
> partition
> > regardless of MTD_PARTITIONED_MASTER flag.
> 
> I am not sure I follow you. I am proposing to create the mtd_master
> device in all cases. I believe this is the future-proof approach. Can
> you make this change?
> 
> Regarding the hierarchy, I indeed agree with what you propose:
> mtd_master parent of whole partition device (if any) parent of
> partitions.
> 

To be sure:

You want to have this hierarchy without MTD_PARTITIONED_MASTER:
mtd_master
\/
|->partition1
|->partition2

With MTD_PARTITIONED_MASTER flag:

mtd_master
\/
|->master_partition
   \/
   |->partition1
   |->partition2

- - 
Thanks,
Sasha


> Thanks,
> Miquèl
Usyskin, Alexander April 10, 2025, 2:12 p.m. UTC | #5
> Subject: RE: [PATCH v7 01/12] mtd: core: always create master device
> 
> Hi
> 
> > Hello,
> >
> > > The mtd_master is completely different class to avoid mtd tree
> disturbances.
> > > It is real kernel device object, I'm not sure how we can do 'link to'
> > > magic here.
> >
> > Maybe we can add that later if someone needs.
> >
> > > About MTD_PARTITIONED_MASTER - we can treat it as another partition
> > and
> > > create master device plus whole device partition as it's child with all other
> > > partitions as children of master device.
> > > For unpartitioned device this mean that we create master device and
> > partition
> > > regardless of MTD_PARTITIONED_MASTER flag.
> >
> > I am not sure I follow you. I am proposing to create the mtd_master
> > device in all cases. I believe this is the future-proof approach. Can
> > you make this change?
> >
> > Regarding the hierarchy, I indeed agree with what you propose:
> > mtd_master parent of whole partition device (if any) parent of
> > partitions.
> >
> 
> To be sure:
> 
> You want to have this hierarchy without MTD_PARTITIONED_MASTER:
> mtd_master
> \/
> |->partition1
> |->partition2
> 
> With MTD_PARTITIONED_MASTER flag:
> 
> mtd_master
> \/
> |->master_partition
>    \/
>    |->partition1
>    |->partition2
> 

Like this?

diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 8dc4f5c493fc..391d81ad960c 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -559,7 +559,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
 		/* Sanitize user input */
 		p.devname[BLKPG_DEVNAMELTH - 1] = '\0';
 
-		return mtd_add_partition(mtd, p.devname, p.start, p.length);
+		return mtd_add_partition(mtd, p.devname, p.start, p.length, NULL);
 
 	case BLKPG_DEL_PARTITION:
 
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index ee46766d74f1..a32cea0ba535 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1108,6 +1108,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 			      const struct mtd_partition *parts,
 			      int nr_parts)
 {
+	struct mtd_info *parent;
 	int ret, err;
 
 	mtd_set_dev_defaults(mtd);
@@ -1116,17 +1117,31 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 	if (ret)
 		goto out;
 
+	ret = add_mtd_device(mtd, false);
+	if (ret)
+		goto out;
+
+	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+	{
+		ret = mtd_add_partition(mtd, dev_name(&mtd->dev), 0, MTDPART_SIZ_FULL, &parent);
+		if (ret)
+			goto out;
+
+	} else {
+		parent = mtd;
+	}
+
 	/* Prefer parsed partitions over driver-provided fallback */
-	ret = parse_mtd_partitions(mtd, types, parser_data);
+	ret = parse_mtd_partitions(parent, types, parser_data);
 	if (ret == -EPROBE_DEFER)
 		goto out;
 
 	if (ret > 0)
 		ret = 0;
 	else if (nr_parts)
-		ret = add_mtd_partitions(mtd, parts, nr_parts);
-	else
-		ret = add_mtd_device(mtd, true);
+		ret = add_mtd_partitions(parent, parts, nr_parts);
+	else if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+		ret = mtd_add_partition(parent, dev_name(&mtd->dev), 0, MTDPART_SIZ_FULL, NULL);
 
 	if (ret)
 		goto out;
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 8a019cf0360d..cd5ae919b80c 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -242,7 +242,7 @@ static int mtd_add_partition_attrs(struct mtd_info *new)
 }
 
 int mtd_add_partition(struct mtd_info *parent, const char *name,
-		      long long offset, long long length)
+		      long long offset, long long length, struct mtd_info **out)
 {
 	struct mtd_info *master = mtd_get_master(parent);
 	u64 parent_size = mtd_is_partition(parent) ?
@@ -281,6 +281,9 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
 
 	mtd_add_partition_attrs(child);
 
+	if (*out)
+		*out = child;
+
 	return 0;
 
 err_remove_part:
@@ -401,12 +404,6 @@ int add_mtd_partitions(struct mtd_info *parent,
 	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n",
 	       nbparts, parent->name);
 
-	if (!mtd_is_partition(parent)) {
-		ret = add_mtd_device(parent, IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER));
-		if (ret)
-			return ret;
-	}
-
 	for (i = 0; i < nbparts; i++) {
 		child = allocate_partition(parent, parts + i, i, cur_offset);
 		if (IS_ERR(child)) {
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index b74a539ec581..5daf80df9e89 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -108,7 +108,7 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser);
 		      deregister_mtd_parser)
 
 int mtd_add_partition(struct mtd_info *master, const char *name,
-		      long long offset, long long length);
+		      long long offset, long long length, struct mtd_info **part);
 int mtd_del_partition(struct mtd_info *master, int partno);
 uint64_t mtd_get_device_size(const struct mtd_info *mtd);
diff mbox series

Patch

diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 724f917f91ba..d0e7fb027eb6 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -68,7 +68,13 @@  static struct class mtd_class = {
 	.pm = MTD_CLS_PM_OPS,
 };
 
+static struct class mtd_master_class = {
+	.name = "mtd_master",
+	.pm = MTD_CLS_PM_OPS,
+};
+
 static DEFINE_IDR(mtd_idr);
+static DEFINE_IDR(mtd_master_idr);
 
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
@@ -83,8 +89,9 @@  EXPORT_SYMBOL_GPL(__mtd_next_device);
 
 static LIST_HEAD(mtd_notifiers);
 
-
+#define MTD_MASTER_DEVS 255
 #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
+static dev_t mtd_master_devt;
 
 /* REVISIT once MTD uses the driver model better, whoever allocates
  * the mtd_info will probably want to use the release() hook...
@@ -104,6 +111,17 @@  static void mtd_release(struct device *dev)
 	device_destroy(&mtd_class, index + 1);
 }
 
+static void mtd_master_release(struct device *dev)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	idr_remove(&mtd_master_idr, mtd->index);
+	of_node_put(mtd_get_of_node(mtd));
+
+	if (mtd_is_partition(mtd))
+		release_mtd_partition(mtd);
+}
+
 static void mtd_device_release(struct kref *kref)
 {
 	struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
@@ -367,6 +385,11 @@  static const struct device_type mtd_devtype = {
 	.release	= mtd_release,
 };
 
+static const struct device_type mtd_master_devtype = {
+	.name		= "mtd_master",
+	.release	= mtd_master_release,
+};
+
 static bool mtd_expert_analysis_mode;
 
 #ifdef CONFIG_DEBUG_FS
@@ -634,13 +657,13 @@  static void mtd_check_of_node(struct mtd_info *mtd)
 /**
  *	add_mtd_device - register an MTD device
  *	@mtd: pointer to new MTD device info structure
+ *	@partitioned: create partitioned device
  *
  *	Add a device to the list of MTD devices present in the system, and
  *	notify each currently active MTD 'user' of its arrival. Returns
  *	zero on success or non-zero on failure.
  */
-
-int add_mtd_device(struct mtd_info *mtd)
+int add_mtd_device(struct mtd_info *mtd, bool partitioned)
 {
 	struct device_node *np = mtd_get_of_node(mtd);
 	struct mtd_info *master = mtd_get_master(mtd);
@@ -687,10 +710,17 @@  int add_mtd_device(struct mtd_info *mtd)
 	ofidx = -1;
 	if (np)
 		ofidx = of_alias_get_id(np, "mtd");
-	if (ofidx >= 0)
-		i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
-	else
-		i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
+	if (partitioned) {
+		if (ofidx >= 0)
+			i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
+		else
+			i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
+	} else {
+		if (ofidx >= 0)
+			i = idr_alloc(&mtd_master_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
+		else
+			i = idr_alloc(&mtd_master_idr, mtd, 0, 0, GFP_KERNEL);
+	}
 	if (i < 0) {
 		error = i;
 		goto fail_locked;
@@ -738,15 +768,23 @@  int add_mtd_device(struct mtd_info *mtd)
 	/* Caller should have set dev.parent to match the
 	 * physical device, if appropriate.
 	 */
-	mtd->dev.type = &mtd_devtype;
-	mtd->dev.class = &mtd_class;
-	mtd->dev.devt = MTD_DEVT(i);
-	dev_set_name(&mtd->dev, "mtd%d", i);
+	if (partitioned) {
+		mtd->dev.type = &mtd_devtype;
+		mtd->dev.class = &mtd_class;
+		mtd->dev.devt = MTD_DEVT(i);
+		dev_set_name(&mtd->dev, "mtd%d", i);
+	} else {
+		mtd->dev.type = &mtd_master_devtype;
+		mtd->dev.class = &mtd_master_class;
+		mtd->dev.devt = MKDEV(MAJOR(mtd_master_devt), i);
+		dev_set_name(&mtd->dev, "mtd_master%d", i);
+	}
 	dev_set_drvdata(&mtd->dev, mtd);
 	mtd_check_of_node(mtd);
 	of_node_get(mtd_get_of_node(mtd));
 	error = device_register(&mtd->dev);
 	if (error) {
+		pr_err("mtd: %s device_register fail %d\n", mtd->name, error);
 		put_device(&mtd->dev);
 		goto fail_added;
 	}
@@ -758,10 +796,13 @@  int add_mtd_device(struct mtd_info *mtd)
 
 	mtd_debugfs_populate(mtd);
 
-	device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
-		      "mtd%dro", i);
+	if (partitioned) {
+		device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
+			      "mtd%dro", i);
+	}
 
-	pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
+	pr_debug("mtd: Giving out %spartitioned device %d to %s\n",
+		 partitioned ? "" : "un-", i, mtd->name);
 	/* No need to get a refcount on the module containing
 	   the notifier, since we hold the mtd_table_mutex */
 	list_for_each_entry(not, &mtd_notifiers, list)
@@ -769,13 +810,16 @@  int add_mtd_device(struct mtd_info *mtd)
 
 	mutex_unlock(&mtd_table_mutex);
 
-	if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs")) {
-		if (IS_BUILTIN(CONFIG_MTD)) {
-			pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
-			ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
-		} else {
-			pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
-				mtd->index, mtd->name);
+	if (partitioned) {
+		if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs")) {
+			if (IS_BUILTIN(CONFIG_MTD)) {
+				pr_info("mtd: setting mtd%d (%s) as root device\n",
+					mtd->index, mtd->name);
+				ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
+			} else {
+				pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
+					mtd->index, mtd->name);
+			}
 		}
 	}
 
@@ -790,7 +834,10 @@  int add_mtd_device(struct mtd_info *mtd)
 	device_unregister(&mtd->dev);
 fail_added:
 	of_node_put(mtd_get_of_node(mtd));
-	idr_remove(&mtd_idr, i);
+	if (partitioned)
+		idr_remove(&mtd_idr, i);
+	else
+		idr_remove(&mtd_master_idr, i);
 fail_locked:
 	mutex_unlock(&mtd_table_mutex);
 	return error;
@@ -808,12 +855,14 @@  int add_mtd_device(struct mtd_info *mtd)
 
 int del_mtd_device(struct mtd_info *mtd)
 {
-	int ret;
 	struct mtd_notifier *not;
+	struct idr *idr;
+	int ret;
 
 	mutex_lock(&mtd_table_mutex);
 
-	if (idr_find(&mtd_idr, mtd->index) != mtd) {
+	idr = mtd->dev.class == &mtd_class ? &mtd_idr : &mtd_master_idr;
+	if (idr_find(idr, mtd->index) != mtd) {
 		ret = -ENODEV;
 		goto out_error;
 	}
@@ -1061,12 +1110,6 @@  int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 	if (ret)
 		goto out;
 
-	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
-		ret = add_mtd_device(mtd);
-		if (ret)
-			goto out;
-	}
-
 	/* Prefer parsed partitions over driver-provided fallback */
 	ret = parse_mtd_partitions(mtd, types, parser_data);
 	if (ret == -EPROBE_DEFER)
@@ -1076,10 +1119,8 @@  int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 		ret = 0;
 	else if (nr_parts)
 		ret = add_mtd_partitions(mtd, parts, nr_parts);
-	else if (!device_is_registered(&mtd->dev))
-		ret = add_mtd_device(mtd);
 	else
-		ret = 0;
+		ret = add_mtd_device(mtd, true);
 
 	if (ret)
 		goto out;
@@ -1099,13 +1140,14 @@  int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 		register_reboot_notifier(&mtd->reboot_notifier);
 	}
 
+	return 0;
 out:
-	if (ret) {
-		nvmem_unregister(mtd->otp_user_nvmem);
-		nvmem_unregister(mtd->otp_factory_nvmem);
-	}
+	nvmem_unregister(mtd->otp_user_nvmem);
+	nvmem_unregister(mtd->otp_factory_nvmem);
 
-	if (ret && device_is_registered(&mtd->dev))
+	del_mtd_partitions(mtd);
+
+	if (device_is_registered(&mtd->dev))
 		del_mtd_device(mtd);
 
 	return ret;
@@ -1261,8 +1303,7 @@  int __get_mtd_device(struct mtd_info *mtd)
 		mtd = mtd->parent;
 	}
 
-	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
-		kref_get(&master->refcnt);
+	kref_get(&master->refcnt);
 
 	return 0;
 }
@@ -1356,8 +1397,7 @@  void __put_mtd_device(struct mtd_info *mtd)
 		mtd = parent;
 	}
 
-	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
-		kref_put(&master->refcnt, mtd_device_release);
+	kref_put(&master->refcnt, mtd_device_release);
 
 	module_put(master->owner);
 
@@ -2524,6 +2564,16 @@  static int __init init_mtd(void)
 	if (ret)
 		goto err_reg;
 
+	ret = class_register(&mtd_master_class);
+	if (ret)
+		goto err_reg2;
+
+	ret = alloc_chrdev_region(&mtd_master_devt, 0, MTD_MASTER_DEVS, "mtd_master");
+	if (ret < 0) {
+		pr_err("unable to allocate char dev region\n");
+		goto err_chrdev;
+	}
+
 	mtd_bdi = mtd_bdi_init("mtd");
 	if (IS_ERR(mtd_bdi)) {
 		ret = PTR_ERR(mtd_bdi);
@@ -2548,6 +2598,10 @@  static int __init init_mtd(void)
 	bdi_unregister(mtd_bdi);
 	bdi_put(mtd_bdi);
 err_bdi:
+	unregister_chrdev_region(mtd_master_devt, MTD_MASTER_DEVS);
+err_chrdev:
+	class_unregister(&mtd_master_class);
+err_reg2:
 	class_unregister(&mtd_class);
 err_reg:
 	pr_err("Error registering mtd class or bdi: %d\n", ret);
@@ -2561,9 +2615,12 @@  static void __exit cleanup_mtd(void)
 	if (proc_mtd)
 		remove_proc_entry("mtd", NULL);
 	class_unregister(&mtd_class);
+	class_unregister(&mtd_master_class);
+	unregister_chrdev_region(mtd_master_devt, MTD_MASTER_DEVS);
 	bdi_unregister(mtd_bdi);
 	bdi_put(mtd_bdi);
 	idr_destroy(&mtd_idr);
+	idr_destroy(&mtd_master_idr);
 }
 
 module_init(init_mtd);
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index b014861a06a6..2258d31c5aa6 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -8,7 +8,7 @@  extern struct mutex mtd_table_mutex;
 extern struct backing_dev_info *mtd_bdi;
 
 struct mtd_info *__mtd_next_device(int i);
-int __must_check add_mtd_device(struct mtd_info *mtd);
+int __must_check add_mtd_device(struct mtd_info *mtd, bool partitioned);
 int del_mtd_device(struct mtd_info *mtd);
 int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
 int del_mtd_partitions(struct mtd_info *);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 6811a714349d..97505b132313 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -86,8 +86,7 @@  static struct mtd_info *allocate_partition(struct mtd_info *parent,
 	 * parent conditional on that option. Note, this is a way to
 	 * distinguish between the parent and its partitions in sysfs.
 	 */
-	child->dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
-			    &parent->dev : parent->dev.parent;
+	child->dev.parent = &parent->dev;
 	child->dev.of_node = part->of_node;
 	child->parent = parent;
 	child->part.offset = part->offset;
@@ -276,7 +275,7 @@  int mtd_add_partition(struct mtd_info *parent, const char *name,
 	list_add_tail(&child->part.node, &parent->partitions);
 	mutex_unlock(&master->master.partitions_lock);
 
-	ret = add_mtd_device(child);
+	ret = add_mtd_device(child, true);
 	if (ret)
 		goto err_remove_part;
 
@@ -402,6 +401,12 @@  int add_mtd_partitions(struct mtd_info *parent,
 	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n",
 	       nbparts, parent->name);
 
+	if (!mtd_is_partition(parent)) {
+		ret = add_mtd_device(parent, IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER));
+		if (ret)
+			return ret;
+	}
+
 	for (i = 0; i < nbparts; i++) {
 		child = allocate_partition(parent, parts + i, i, cur_offset);
 		if (IS_ERR(child)) {
@@ -413,7 +418,7 @@  int add_mtd_partitions(struct mtd_info *parent,
 		list_add_tail(&child->part.node, &parent->partitions);
 		mutex_unlock(&master->master.partitions_lock);
 
-		ret = add_mtd_device(child);
+		ret = add_mtd_device(child, true);
 		if (ret) {
 			mutex_lock(&master->master.partitions_lock);
 			list_del(&child->part.node);
@@ -590,9 +595,6 @@  static int mtd_part_of_parse(struct mtd_info *master,
 	int ret, err = 0;
 
 	dev = &master->dev;
-	/* Use parent device (controller) if the top level MTD is not registered */
-	if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master))
-		dev = master->dev.parent;
 
 	np = mtd_get_of_node(master);
 	if (mtd_is_partition(master))
@@ -712,6 +714,7 @@  int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
 		if (ret < 0 && !err)
 			err = ret;
 	}
+
 	return err;
 }