Commit 0aa7b390 authored by Alexander Usyskin's avatar Alexander Usyskin Committed by Miquel Raynal
Browse files

mtd: core: always create master device



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: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
parent 91b7163b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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:

+112 −40
Original line number Diff line number Diff line
@@ -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 (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,10 +768,18 @@ int add_mtd_device(struct mtd_info *mtd)
	/* Caller should have set dev.parent to match the
	 * physical device, if appropriate.
	 */
	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);
		error = 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);
		error = dev_set_name(&mtd->dev, "mtd_master%d", i);
	}
	if (error)
		goto fail_devname;
	dev_set_drvdata(&mtd->dev, mtd);
@@ -749,6 +787,7 @@ int add_mtd_device(struct mtd_info *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;
	}
@@ -760,10 +799,13 @@ int add_mtd_device(struct mtd_info *mtd)

	mtd_debugfs_populate(mtd);

	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)
@@ -771,15 +813,18 @@ int add_mtd_device(struct mtd_info *mtd)

	mutex_unlock(&mtd_table_mutex);

	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);
				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);
			}
		}
	}

	/* We _know_ we aren't being removed, because
	   our caller is still holding us here. So none
@@ -793,7 +838,10 @@ int add_mtd_device(struct mtd_info *mtd)
fail_added:
	of_node_put(mtd_get_of_node(mtd));
fail_devname:
	if (partitioned)
		idr_remove(&mtd_idr, i);
	else
		idr_remove(&mtd_master_idr, i);
fail_locked:
	mutex_unlock(&mtd_table_mutex);
	return error;
@@ -811,12 +859,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;
	}
@@ -1056,6 +1106,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);
@@ -1064,25 +1115,30 @@ 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 = add_mtd_device(mtd);
		ret = mtd_add_partition(mtd, mtd->name, 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 if (!device_is_registered(&mtd->dev))
		ret = add_mtd_device(mtd);
	else
		ret = 0;
		ret = add_mtd_partitions(parent, parts, nr_parts);
	else if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
		ret = mtd_add_partition(parent, mtd->name, 0, MTDPART_SIZ_FULL, NULL);

	if (ret)
		goto out;
@@ -1102,13 +1158,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);
	}

	if (ret && device_is_registered(&mtd->dev)) {
	del_mtd_partitions(mtd);

	if (device_is_registered(&mtd->dev)) {
		err = del_mtd_device(mtd);
		if (err)
			pr_err("Error when deleting MTD device (%d)\n", err);
@@ -1267,7 +1324,6 @@ int __get_mtd_device(struct mtd_info *mtd)
		mtd = mtd->parent;
	}

	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
	kref_get(&master->refcnt);

	return 0;
@@ -1362,7 +1418,6 @@ void __put_mtd_device(struct mtd_info *mtd)
		mtd = parent;
	}

	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
	kref_put(&master->refcnt, mtd_device_release);

	module_put(master->owner);
@@ -2530,6 +2585,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);
@@ -2554,6 +2619,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);
@@ -2567,9 +2636,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);
+1 −1
Original line number Diff line number Diff line
@@ -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 *);
+8 −8
Original line number Diff line number Diff line
@@ -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;
@@ -243,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) ?
@@ -276,12 +275,15 @@ 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;

	mtd_add_partition_attrs(child);

	if (out)
		*out = child;

	return 0;

err_remove_part:
@@ -413,7 +415,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 +592,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))
@@ -711,6 +710,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
		if (ret < 0 && !err)
			err = ret;
	}

	return err;
}

+1 −1
Original line number Diff line number Diff line
@@ -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);