@@ -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 %spartitoned 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;
@@ -1061,12 +1108,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 +1117,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 +1138,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);
+
+ del_mtd_partitions(mtd);
- if (ret && device_is_registered(&mtd->dev))
+ if (device_is_registered(&mtd->dev))
del_mtd_device(mtd);
return ret;
@@ -1261,8 +1301,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 +1395,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 +2562,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 +2596,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 +2613,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);
@@ -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 *);
@@ -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;
}
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 | 135 +++++++++++++++++++++++++++++------------- drivers/mtd/mtdcore.h | 2 +- drivers/mtd/mtdpart.c | 17 +++--- 3 files changed, 106 insertions(+), 48 deletions(-)