xref: /linux/drivers/mtd/mtd_virt_concat.c (revision 8541d8f725c673db3bd741947f27974358b2e163)
143db6366SAmit Kumar Mahapatra // SPDX-License-Identifier: GPL-2.0+
243db6366SAmit Kumar Mahapatra /*
343db6366SAmit Kumar Mahapatra  * Virtual concat MTD device driver
443db6366SAmit Kumar Mahapatra  *
543db6366SAmit Kumar Mahapatra  * Copyright (C) 2018 Bernhard Frauendienst
643db6366SAmit Kumar Mahapatra  * Author: Bernhard Frauendienst <kernel@nospam.obeliks.de>
743db6366SAmit Kumar Mahapatra  */
843db6366SAmit Kumar Mahapatra 
943db6366SAmit Kumar Mahapatra #include <linux/device.h>
1043db6366SAmit Kumar Mahapatra #include <linux/mtd/mtd.h>
1143db6366SAmit Kumar Mahapatra #include "mtdcore.h"
1243db6366SAmit Kumar Mahapatra #include <linux/mtd/partitions.h>
1343db6366SAmit Kumar Mahapatra #include <linux/of.h>
1443db6366SAmit Kumar Mahapatra #include <linux/of_platform.h>
1543db6366SAmit Kumar Mahapatra #include <linux/slab.h>
1643db6366SAmit Kumar Mahapatra #include <linux/mtd/concat.h>
1743db6366SAmit Kumar Mahapatra 
1843db6366SAmit Kumar Mahapatra #define CONCAT_PROP "part-concat-next"
1943db6366SAmit Kumar Mahapatra #define CONCAT_POSTFIX "concat"
2043db6366SAmit Kumar Mahapatra #define MIN_DEV_PER_CONCAT 1
2143db6366SAmit Kumar Mahapatra 
2243db6366SAmit Kumar Mahapatra static LIST_HEAD(concat_node_list);
2343db6366SAmit Kumar Mahapatra 
2443db6366SAmit Kumar Mahapatra /**
2543db6366SAmit Kumar Mahapatra  * struct mtd_virt_concat_node - components of a concatenation
2643db6366SAmit Kumar Mahapatra  * @head: List handle
2743db6366SAmit Kumar Mahapatra  * @count: Number of nodes
2843db6366SAmit Kumar Mahapatra  * @nodes: Pointer to the nodes (partitions) to concatenate
2943db6366SAmit Kumar Mahapatra  * @concat: Concatenation container
3043db6366SAmit Kumar Mahapatra  */
3143db6366SAmit Kumar Mahapatra struct mtd_virt_concat_node {
3243db6366SAmit Kumar Mahapatra 	struct list_head head;
3343db6366SAmit Kumar Mahapatra 	unsigned int count;
3443db6366SAmit Kumar Mahapatra 	struct mtd_concat *concat;
35c685e6e8SRosen Penev 	struct device_node *nodes[] __counted_by(count);
3643db6366SAmit Kumar Mahapatra };
3743db6366SAmit Kumar Mahapatra 
3843db6366SAmit Kumar Mahapatra /**
3943db6366SAmit Kumar Mahapatra  * mtd_is_part_concat - Check if the device is already part
4043db6366SAmit Kumar Mahapatra  *                       of a concatenated device
4143db6366SAmit Kumar Mahapatra  * @dev:        pointer to 'device_node'
4243db6366SAmit Kumar Mahapatra  *
4343db6366SAmit Kumar Mahapatra  * Return: true if the device is already part of a concatenation,
4443db6366SAmit Kumar Mahapatra  *         false otherwise.
4543db6366SAmit Kumar Mahapatra  */
4643db6366SAmit Kumar Mahapatra static bool mtd_is_part_concat(struct device_node *dev)
4743db6366SAmit Kumar Mahapatra {
4843db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item;
4943db6366SAmit Kumar Mahapatra 	int idx;
5043db6366SAmit Kumar Mahapatra 
5143db6366SAmit Kumar Mahapatra 	list_for_each_entry(item, &concat_node_list, head) {
5243db6366SAmit Kumar Mahapatra 		for (idx = 0; idx < item->count; idx++) {
5343db6366SAmit Kumar Mahapatra 			if (item->nodes[idx] == dev)
5443db6366SAmit Kumar Mahapatra 				return true;
5543db6366SAmit Kumar Mahapatra 		}
5643db6366SAmit Kumar Mahapatra 	}
5743db6366SAmit Kumar Mahapatra 	return false;
5843db6366SAmit Kumar Mahapatra }
5943db6366SAmit Kumar Mahapatra 
6043db6366SAmit Kumar Mahapatra static void mtd_virt_concat_put_mtd_devices(struct mtd_concat *concat)
6143db6366SAmit Kumar Mahapatra {
6243db6366SAmit Kumar Mahapatra 	int i;
6343db6366SAmit Kumar Mahapatra 
6443db6366SAmit Kumar Mahapatra 	for (i = 0; i < concat->num_subdev; i++)
6543db6366SAmit Kumar Mahapatra 		put_mtd_device(concat->subdev[i]);
6643db6366SAmit Kumar Mahapatra }
6743db6366SAmit Kumar Mahapatra 
6843db6366SAmit Kumar Mahapatra void mtd_virt_concat_destroy_joins(void)
6943db6366SAmit Kumar Mahapatra {
7043db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item, *tmp;
7143db6366SAmit Kumar Mahapatra 	struct mtd_info *mtd;
7243db6366SAmit Kumar Mahapatra 
7343db6366SAmit Kumar Mahapatra 	list_for_each_entry_safe(item, tmp, &concat_node_list, head) {
7443db6366SAmit Kumar Mahapatra 		mtd = &item->concat->mtd;
7543db6366SAmit Kumar Mahapatra 		if (item->concat) {
7643db6366SAmit Kumar Mahapatra 			mtd_device_unregister(mtd);
7743db6366SAmit Kumar Mahapatra 			kfree(mtd->name);
7843db6366SAmit Kumar Mahapatra 			mtd_concat_destroy(mtd);
7943db6366SAmit Kumar Mahapatra 			mtd_virt_concat_put_mtd_devices(item->concat);
8043db6366SAmit Kumar Mahapatra 		}
8143db6366SAmit Kumar Mahapatra 	}
8243db6366SAmit Kumar Mahapatra }
8343db6366SAmit Kumar Mahapatra 
8443db6366SAmit Kumar Mahapatra /**
8543db6366SAmit Kumar Mahapatra  * mtd_virt_concat_destroy - Destroy the concat that includes the mtd object
8643db6366SAmit Kumar Mahapatra  * @mtd:        pointer to 'mtd_info'
8743db6366SAmit Kumar Mahapatra  *
8843db6366SAmit Kumar Mahapatra  * Return: 0 on success, -error otherwise.
8943db6366SAmit Kumar Mahapatra  */
9043db6366SAmit Kumar Mahapatra int mtd_virt_concat_destroy(struct mtd_info *mtd)
9143db6366SAmit Kumar Mahapatra {
9243db6366SAmit Kumar Mahapatra 	struct mtd_info *child, *master = mtd_get_master(mtd);
9343db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item, *tmp;
9443db6366SAmit Kumar Mahapatra 	struct mtd_concat *concat;
9543db6366SAmit Kumar Mahapatra 	int idx, ret = 0;
9643db6366SAmit Kumar Mahapatra 	bool is_mtd_found;
9743db6366SAmit Kumar Mahapatra 
9843db6366SAmit Kumar Mahapatra 	list_for_each_entry_safe(item, tmp, &concat_node_list, head) {
9943db6366SAmit Kumar Mahapatra 		is_mtd_found = false;
10043db6366SAmit Kumar Mahapatra 
10143db6366SAmit Kumar Mahapatra 		/* Find the concat item that hold the mtd device */
10243db6366SAmit Kumar Mahapatra 		for (idx = 0; idx < item->count; idx++) {
10343db6366SAmit Kumar Mahapatra 			if (item->nodes[idx] == mtd->dev.of_node) {
10443db6366SAmit Kumar Mahapatra 				is_mtd_found = true;
10543db6366SAmit Kumar Mahapatra 				break;
10643db6366SAmit Kumar Mahapatra 			}
10743db6366SAmit Kumar Mahapatra 		}
10843db6366SAmit Kumar Mahapatra 		if (!is_mtd_found)
10943db6366SAmit Kumar Mahapatra 			continue;
11043db6366SAmit Kumar Mahapatra 		concat = item->concat;
11143db6366SAmit Kumar Mahapatra 
11243db6366SAmit Kumar Mahapatra 		/*
11343db6366SAmit Kumar Mahapatra 		 * Since this concatenated device is being removed, retrieve
11443db6366SAmit Kumar Mahapatra 		 * all MTD devices that are part of it and register them
11543db6366SAmit Kumar Mahapatra 		 * individually.
11643db6366SAmit Kumar Mahapatra 		 */
11743db6366SAmit Kumar Mahapatra 		for (idx = 0; idx < concat->num_subdev; idx++) {
11843db6366SAmit Kumar Mahapatra 			child = concat->subdev[idx];
11943db6366SAmit Kumar Mahapatra 			if (child->dev.of_node != mtd->dev.of_node) {
12043db6366SAmit Kumar Mahapatra 				ret = add_mtd_device(child);
12143db6366SAmit Kumar Mahapatra 				if (ret)
12243db6366SAmit Kumar Mahapatra 					goto out;
12343db6366SAmit Kumar Mahapatra 			}
12443db6366SAmit Kumar Mahapatra 		}
12543db6366SAmit Kumar Mahapatra 		/* Destroy the concat */
12643db6366SAmit Kumar Mahapatra 		if (concat->mtd.name) {
12743db6366SAmit Kumar Mahapatra 			del_mtd_device(&concat->mtd);
12843db6366SAmit Kumar Mahapatra 			kfree(concat->mtd.name);
12943db6366SAmit Kumar Mahapatra 			mtd_concat_destroy(&concat->mtd);
13043db6366SAmit Kumar Mahapatra 			mtd_virt_concat_put_mtd_devices(item->concat);
13143db6366SAmit Kumar Mahapatra 		}
13243db6366SAmit Kumar Mahapatra 
13343db6366SAmit Kumar Mahapatra 		for (idx = 0; idx < item->count; idx++)
13443db6366SAmit Kumar Mahapatra 			of_node_put(item->nodes[idx]);
13543db6366SAmit Kumar Mahapatra 
13643db6366SAmit Kumar Mahapatra 		kfree(item);
13743db6366SAmit Kumar Mahapatra 	}
13843db6366SAmit Kumar Mahapatra 	return 0;
13943db6366SAmit Kumar Mahapatra out:
14043db6366SAmit Kumar Mahapatra 	mutex_lock(&master->master.partitions_lock);
14143db6366SAmit Kumar Mahapatra 	list_del(&child->part.node);
14243db6366SAmit Kumar Mahapatra 	mutex_unlock(&master->master.partitions_lock);
14343db6366SAmit Kumar Mahapatra 	kfree(mtd->name);
14443db6366SAmit Kumar Mahapatra 	kfree(mtd);
14543db6366SAmit Kumar Mahapatra 
14643db6366SAmit Kumar Mahapatra 	return ret;
14743db6366SAmit Kumar Mahapatra }
14843db6366SAmit Kumar Mahapatra 
14943db6366SAmit Kumar Mahapatra /**
15043db6366SAmit Kumar Mahapatra  * mtd_virt_concat_create_item - Create a concat item
15143db6366SAmit Kumar Mahapatra  * @parts:        pointer to 'device_node'
15243db6366SAmit Kumar Mahapatra  * @count:        number of mtd devices that make up
15343db6366SAmit Kumar Mahapatra  *                the concatenated device.
15443db6366SAmit Kumar Mahapatra  *
15543db6366SAmit Kumar Mahapatra  * Return: 0 on success, -error otherwise.
15643db6366SAmit Kumar Mahapatra  */
15743db6366SAmit Kumar Mahapatra static int mtd_virt_concat_create_item(struct device_node *parts,
15843db6366SAmit Kumar Mahapatra 				       unsigned int count)
15943db6366SAmit Kumar Mahapatra {
16043db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item;
16143db6366SAmit Kumar Mahapatra 	struct mtd_concat *concat;
16243db6366SAmit Kumar Mahapatra 	int i;
16343db6366SAmit Kumar Mahapatra 
16443db6366SAmit Kumar Mahapatra 	for (i = 0; i < (count - 1); i++) {
16543db6366SAmit Kumar Mahapatra 		if (mtd_is_part_concat(of_parse_phandle(parts, CONCAT_PROP, i)))
16643db6366SAmit Kumar Mahapatra 			return 0;
16743db6366SAmit Kumar Mahapatra 	}
16843db6366SAmit Kumar Mahapatra 
169c685e6e8SRosen Penev 	item = kzalloc_flex(*item, nodes, count, GFP_KERNEL);
17043db6366SAmit Kumar Mahapatra 	if (!item)
17143db6366SAmit Kumar Mahapatra 		return -ENOMEM;
17243db6366SAmit Kumar Mahapatra 
17343db6366SAmit Kumar Mahapatra 	item->count = count;
17443db6366SAmit Kumar Mahapatra 
17543db6366SAmit Kumar Mahapatra 	/*
17643db6366SAmit Kumar Mahapatra 	 * The partition in which "part-concat-next" property
17743db6366SAmit Kumar Mahapatra 	 * is defined is the first device in the list of concat
17843db6366SAmit Kumar Mahapatra 	 * devices.
17943db6366SAmit Kumar Mahapatra 	 */
18043db6366SAmit Kumar Mahapatra 	item->nodes[0] = parts;
18143db6366SAmit Kumar Mahapatra 
18243db6366SAmit Kumar Mahapatra 	for (i = 1; i < count; i++)
18343db6366SAmit Kumar Mahapatra 		item->nodes[i] = of_parse_phandle(parts, CONCAT_PROP, (i - 1));
18443db6366SAmit Kumar Mahapatra 
185*e19eaffcSRosen Penev 	concat = kzalloc_flex(*concat, subdev, count, GFP_KERNEL);
18643db6366SAmit Kumar Mahapatra 	if (!concat) {
18743db6366SAmit Kumar Mahapatra 		kfree(item);
18843db6366SAmit Kumar Mahapatra 		return -ENOMEM;
18943db6366SAmit Kumar Mahapatra 	}
19043db6366SAmit Kumar Mahapatra 
19143db6366SAmit Kumar Mahapatra 	item->concat = concat;
19243db6366SAmit Kumar Mahapatra 
19343db6366SAmit Kumar Mahapatra 	list_add_tail(&item->head, &concat_node_list);
19443db6366SAmit Kumar Mahapatra 
19543db6366SAmit Kumar Mahapatra 	return 0;
19643db6366SAmit Kumar Mahapatra }
19743db6366SAmit Kumar Mahapatra 
19843db6366SAmit Kumar Mahapatra void mtd_virt_concat_destroy_items(void)
19943db6366SAmit Kumar Mahapatra {
20043db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item, *temp;
20143db6366SAmit Kumar Mahapatra 	int i;
20243db6366SAmit Kumar Mahapatra 
20343db6366SAmit Kumar Mahapatra 	list_for_each_entry_safe(item, temp, &concat_node_list, head) {
20443db6366SAmit Kumar Mahapatra 		for (i = 0; i < item->count; i++)
20543db6366SAmit Kumar Mahapatra 			of_node_put(item->nodes[i]);
20643db6366SAmit Kumar Mahapatra 
20743db6366SAmit Kumar Mahapatra 		kfree(item);
20843db6366SAmit Kumar Mahapatra 	}
20943db6366SAmit Kumar Mahapatra }
21043db6366SAmit Kumar Mahapatra 
21143db6366SAmit Kumar Mahapatra /**
21287d8f128SLuca Ceresoli  * mtd_virt_concat_add - Add a mtd device to the concat list
21343db6366SAmit Kumar Mahapatra  * @mtd:        pointer to 'mtd_info'
21443db6366SAmit Kumar Mahapatra  *
21543db6366SAmit Kumar Mahapatra  * Return: true on success, false otherwise.
21643db6366SAmit Kumar Mahapatra  */
21743db6366SAmit Kumar Mahapatra bool mtd_virt_concat_add(struct mtd_info *mtd)
21843db6366SAmit Kumar Mahapatra {
21943db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item;
22043db6366SAmit Kumar Mahapatra 	struct mtd_concat *concat;
22143db6366SAmit Kumar Mahapatra 	int idx;
22243db6366SAmit Kumar Mahapatra 
22343db6366SAmit Kumar Mahapatra 	list_for_each_entry(item, &concat_node_list, head) {
22443db6366SAmit Kumar Mahapatra 		concat = item->concat;
22543db6366SAmit Kumar Mahapatra 		for (idx = 0; idx < item->count; idx++) {
22643db6366SAmit Kumar Mahapatra 			if (item->nodes[idx] == mtd->dev.of_node) {
22743db6366SAmit Kumar Mahapatra 				concat->subdev[concat->num_subdev++] = mtd;
22843db6366SAmit Kumar Mahapatra 				return true;
22943db6366SAmit Kumar Mahapatra 			}
23043db6366SAmit Kumar Mahapatra 		}
23143db6366SAmit Kumar Mahapatra 	}
23243db6366SAmit Kumar Mahapatra 	return false;
23343db6366SAmit Kumar Mahapatra }
23443db6366SAmit Kumar Mahapatra 
23543db6366SAmit Kumar Mahapatra /**
23643db6366SAmit Kumar Mahapatra  * mtd_virt_concat_node_create - List all the concatenations found in DT
23743db6366SAmit Kumar Mahapatra  *
23843db6366SAmit Kumar Mahapatra  * Return: 0 on success, -error otherwise.
23943db6366SAmit Kumar Mahapatra  */
24043db6366SAmit Kumar Mahapatra int mtd_virt_concat_node_create(void)
24143db6366SAmit Kumar Mahapatra {
24243db6366SAmit Kumar Mahapatra 	struct device_node *parts = NULL;
24343db6366SAmit Kumar Mahapatra 	int ret = 0, count = 0;
24443db6366SAmit Kumar Mahapatra 
24543db6366SAmit Kumar Mahapatra 	/* List all the concatenations found in DT */
24643db6366SAmit Kumar Mahapatra 	do {
24743db6366SAmit Kumar Mahapatra 		parts = of_find_node_with_property(parts, CONCAT_PROP);
24843db6366SAmit Kumar Mahapatra 		if (!of_device_is_available(parts))
24943db6366SAmit Kumar Mahapatra 			continue;
25043db6366SAmit Kumar Mahapatra 
25143db6366SAmit Kumar Mahapatra 		if (mtd_is_part_concat(parts))
25243db6366SAmit Kumar Mahapatra 			continue;
25343db6366SAmit Kumar Mahapatra 
25443db6366SAmit Kumar Mahapatra 		count = of_count_phandle_with_args(parts, CONCAT_PROP, NULL);
25543db6366SAmit Kumar Mahapatra 		if (count < MIN_DEV_PER_CONCAT)
25643db6366SAmit Kumar Mahapatra 			continue;
25743db6366SAmit Kumar Mahapatra 
25843db6366SAmit Kumar Mahapatra 		/*
25943db6366SAmit Kumar Mahapatra 		 * The partition in which "part-concat-next" property is defined
26043db6366SAmit Kumar Mahapatra 		 * is also part of the concat device, so increament count by 1.
26143db6366SAmit Kumar Mahapatra 		 */
26243db6366SAmit Kumar Mahapatra 		count++;
26343db6366SAmit Kumar Mahapatra 
26443db6366SAmit Kumar Mahapatra 		ret = mtd_virt_concat_create_item(parts, count);
26543db6366SAmit Kumar Mahapatra 		if (ret) {
26643db6366SAmit Kumar Mahapatra 			of_node_put(parts);
26743db6366SAmit Kumar Mahapatra 			goto destroy_items;
26843db6366SAmit Kumar Mahapatra 		}
26943db6366SAmit Kumar Mahapatra 	} while (parts);
27043db6366SAmit Kumar Mahapatra 
27143db6366SAmit Kumar Mahapatra 	return ret;
27243db6366SAmit Kumar Mahapatra 
27343db6366SAmit Kumar Mahapatra destroy_items:
27443db6366SAmit Kumar Mahapatra 	mtd_virt_concat_destroy_items();
27543db6366SAmit Kumar Mahapatra 
27643db6366SAmit Kumar Mahapatra 	return ret;
27743db6366SAmit Kumar Mahapatra }
27843db6366SAmit Kumar Mahapatra 
27943db6366SAmit Kumar Mahapatra /**
28043db6366SAmit Kumar Mahapatra  * mtd_virt_concat_create_join - Create and register the concatenated
28143db6366SAmit Kumar Mahapatra  *                                 MTD device.
28243db6366SAmit Kumar Mahapatra  *
28343db6366SAmit Kumar Mahapatra  * Return: 0 on success, -error otherwise.
28443db6366SAmit Kumar Mahapatra  */
28543db6366SAmit Kumar Mahapatra int mtd_virt_concat_create_join(void)
28643db6366SAmit Kumar Mahapatra {
28743db6366SAmit Kumar Mahapatra 	struct mtd_virt_concat_node *item;
28843db6366SAmit Kumar Mahapatra 	struct mtd_concat *concat;
28943db6366SAmit Kumar Mahapatra 	struct mtd_info *mtd;
29043db6366SAmit Kumar Mahapatra 	ssize_t name_sz;
29143db6366SAmit Kumar Mahapatra 	int ret, idx;
29243db6366SAmit Kumar Mahapatra 	char *name;
29343db6366SAmit Kumar Mahapatra 
29443db6366SAmit Kumar Mahapatra 	list_for_each_entry(item, &concat_node_list, head) {
29543db6366SAmit Kumar Mahapatra 		concat = item->concat;
29643db6366SAmit Kumar Mahapatra 		/*
29743db6366SAmit Kumar Mahapatra 		 * Check if item->count != concat->num_subdev, it indicates
29843db6366SAmit Kumar Mahapatra 		 * that the MTD information for all devices included in the
29943db6366SAmit Kumar Mahapatra 		 * concatenation are not handy, concat MTD device can't be
30043db6366SAmit Kumar Mahapatra 		 * created hence switch to next concat device.
30143db6366SAmit Kumar Mahapatra 		 */
30243db6366SAmit Kumar Mahapatra 		if (item->count != concat->num_subdev) {
30343db6366SAmit Kumar Mahapatra 			continue;
30443db6366SAmit Kumar Mahapatra 		} else {
30543db6366SAmit Kumar Mahapatra 			/* Calculate the legth of the name of the virtual device */
30643db6366SAmit Kumar Mahapatra 			for (idx = 0, name_sz = 0; idx < concat->num_subdev; idx++)
30743db6366SAmit Kumar Mahapatra 				name_sz += (strlen(concat->subdev[idx]->name) + 1);
30843db6366SAmit Kumar Mahapatra 			name_sz += strlen(CONCAT_POSTFIX);
30943db6366SAmit Kumar Mahapatra 			name = kmalloc(name_sz + 1, GFP_KERNEL);
31043db6366SAmit Kumar Mahapatra 			if (!name) {
31143db6366SAmit Kumar Mahapatra 				mtd_virt_concat_put_mtd_devices(concat);
31243db6366SAmit Kumar Mahapatra 				return -ENOMEM;
31343db6366SAmit Kumar Mahapatra 			}
31443db6366SAmit Kumar Mahapatra 
31543db6366SAmit Kumar Mahapatra 			ret = 0;
31643db6366SAmit Kumar Mahapatra 			for (idx = 0; idx < concat->num_subdev; idx++) {
31743db6366SAmit Kumar Mahapatra 				ret += sprintf((name + ret), "%s-",
31843db6366SAmit Kumar Mahapatra 					       concat->subdev[idx]->name);
31943db6366SAmit Kumar Mahapatra 			}
32043db6366SAmit Kumar Mahapatra 			sprintf((name + ret), CONCAT_POSTFIX);
32143db6366SAmit Kumar Mahapatra 
32243db6366SAmit Kumar Mahapatra 			if (concat->mtd.name) {
32343db6366SAmit Kumar Mahapatra 				ret = memcmp(concat->mtd.name, name, name_sz);
32443db6366SAmit Kumar Mahapatra 				if (ret == 0)
32543db6366SAmit Kumar Mahapatra 					continue;
32643db6366SAmit Kumar Mahapatra 			}
32743db6366SAmit Kumar Mahapatra 			mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name);
32843db6366SAmit Kumar Mahapatra 			if (!mtd) {
32943db6366SAmit Kumar Mahapatra 				kfree(name);
33043db6366SAmit Kumar Mahapatra 				return -ENXIO;
33143db6366SAmit Kumar Mahapatra 			}
33243db6366SAmit Kumar Mahapatra 			concat->mtd = *mtd;
33343db6366SAmit Kumar Mahapatra 			/* Arbitrary set the first device as parent */
33443db6366SAmit Kumar Mahapatra 			concat->mtd.dev.parent = concat->subdev[0]->dev.parent;
33543db6366SAmit Kumar Mahapatra 			concat->mtd.dev = concat->subdev[0]->dev;
33643db6366SAmit Kumar Mahapatra 
33743db6366SAmit Kumar Mahapatra 			/* Add the mtd device */
33843db6366SAmit Kumar Mahapatra 			ret = add_mtd_device(&concat->mtd);
33943db6366SAmit Kumar Mahapatra 			if (ret)
34043db6366SAmit Kumar Mahapatra 				goto destroy_concat;
34143db6366SAmit Kumar Mahapatra 		}
34243db6366SAmit Kumar Mahapatra 	}
34343db6366SAmit Kumar Mahapatra 
34443db6366SAmit Kumar Mahapatra 	return 0;
34543db6366SAmit Kumar Mahapatra 
34643db6366SAmit Kumar Mahapatra destroy_concat:
34743db6366SAmit Kumar Mahapatra 	mtd_concat_destroy(mtd);
34843db6366SAmit Kumar Mahapatra 
34943db6366SAmit Kumar Mahapatra 	return ret;
35043db6366SAmit Kumar Mahapatra }
351