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