| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Simple MTD partitioning layer | 
 | 3 |  * | 
| David Woodhouse | a1452a3 | 2010-08-08 20:58:20 +0100 | [diff] [blame] | 4 |  * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net> | 
 | 5 |  * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de> | 
 | 6 |  * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 |  * | 
| David Woodhouse | a1452a3 | 2010-08-08 20:58:20 +0100 | [diff] [blame] | 8 |  * This program is free software; you can redistribute it and/or modify | 
 | 9 |  * it under the terms of the GNU General Public License as published by | 
 | 10 |  * the Free Software Foundation; either version 2 of the License, or | 
 | 11 |  * (at your option) any later version. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 |  * | 
| David Woodhouse | a1452a3 | 2010-08-08 20:58:20 +0100 | [diff] [blame] | 13 |  * This program is distributed in the hope that it will be useful, | 
 | 14 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 15 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 16 |  * GNU General Public License for more details. | 
 | 17 |  * | 
 | 18 |  * You should have received a copy of the GNU General Public License | 
 | 19 |  * along with this program; if not, write to the Free Software | 
 | 20 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
 | 21 |  * | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 22 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 |  | 
 | 24 | #include <linux/module.h> | 
 | 25 | #include <linux/types.h> | 
 | 26 | #include <linux/kernel.h> | 
 | 27 | #include <linux/slab.h> | 
 | 28 | #include <linux/list.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | #include <linux/kmod.h> | 
 | 30 | #include <linux/mtd/mtd.h> | 
 | 31 | #include <linux/mtd/partitions.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 |  | 
 | 33 | /* Our partition linked list */ | 
 | 34 | static LIST_HEAD(mtd_partitions); | 
 | 35 |  | 
 | 36 | /* Our partition node structure */ | 
 | 37 | struct mtd_part { | 
 | 38 | 	struct mtd_info mtd; | 
 | 39 | 	struct mtd_info *master; | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 40 | 	uint64_t offset; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | 	struct list_head list; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | }; | 
 | 43 |  | 
 | 44 | /* | 
 | 45 |  * Given a pointer to the MTD object in the mtd_part structure, we can retrieve | 
 | 46 |  * the pointer to that structure with this macro. | 
 | 47 |  */ | 
 | 48 | #define PART(x)  ((struct mtd_part *)(x)) | 
 | 49 |  | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 50 |  | 
 | 51 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 |  * MTD methods which simply translate the effective address and pass through | 
 | 53 |  * to the _real_ device. | 
 | 54 |  */ | 
 | 55 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 56 | static int part_read(struct mtd_info *mtd, loff_t from, size_t len, | 
 | 57 | 		size_t *retlen, u_char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | { | 
 | 59 | 	struct mtd_part *part = PART(mtd); | 
| Yauhen Kharuzhy | d8877f1 | 2009-03-27 00:41:09 +0200 | [diff] [blame] | 60 | 	struct mtd_ecc_stats stats; | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 61 | 	int res; | 
 | 62 |  | 
| Yauhen Kharuzhy | d8877f1 | 2009-03-27 00:41:09 +0200 | [diff] [blame] | 63 | 	stats = part->master->ecc_stats; | 
 | 64 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | 	if (from >= mtd->size) | 
 | 66 | 		len = 0; | 
 | 67 | 	else if (from + len > mtd->size) | 
 | 68 | 		len = mtd->size - from; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 69 | 	res = part->master->read(part->master, from + part->offset, | 
| Thomas Gleixner | 9223a45 | 2006-05-23 17:21:03 +0200 | [diff] [blame] | 70 | 				   len, retlen, buf); | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 71 | 	if (unlikely(res)) { | 
 | 72 | 		if (res == -EUCLEAN) | 
| Yauhen Kharuzhy | d8877f1 | 2009-03-27 00:41:09 +0200 | [diff] [blame] | 73 | 			mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 74 | 		if (res == -EBADMSG) | 
| Yauhen Kharuzhy | d8877f1 | 2009-03-27 00:41:09 +0200 | [diff] [blame] | 75 | 			mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 76 | 	} | 
 | 77 | 	return res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | } | 
 | 79 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 80 | static int part_point(struct mtd_info *mtd, loff_t from, size_t len, | 
 | 81 | 		size_t *retlen, void **virt, resource_size_t *phys) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 | { | 
 | 83 | 	struct mtd_part *part = PART(mtd); | 
 | 84 | 	if (from >= mtd->size) | 
 | 85 | 		len = 0; | 
 | 86 | 	else if (from + len > mtd->size) | 
 | 87 | 		len = mtd->size - from; | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 88 | 	return part->master->point (part->master, from + part->offset, | 
| Jared Hulbert | a98889f | 2008-04-29 23:26:49 -0700 | [diff] [blame] | 89 | 				    len, retlen, virt, phys); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | } | 
| Thomas Gleixner | 9223a45 | 2006-05-23 17:21:03 +0200 | [diff] [blame] | 91 |  | 
| Jared Hulbert | a98889f | 2008-04-29 23:26:49 -0700 | [diff] [blame] | 92 | static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | { | 
 | 94 | 	struct mtd_part *part = PART(mtd); | 
 | 95 |  | 
| Jared Hulbert | a98889f | 2008-04-29 23:26:49 -0700 | [diff] [blame] | 96 | 	part->master->unpoint(part->master, from + part->offset, len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | } | 
 | 98 |  | 
| David Howells | 402d326 | 2009-02-12 10:40:00 +0000 | [diff] [blame] | 99 | static unsigned long part_get_unmapped_area(struct mtd_info *mtd, | 
 | 100 | 					    unsigned long len, | 
 | 101 | 					    unsigned long offset, | 
 | 102 | 					    unsigned long flags) | 
 | 103 | { | 
 | 104 | 	struct mtd_part *part = PART(mtd); | 
 | 105 |  | 
 | 106 | 	offset += part->offset; | 
 | 107 | 	return part->master->get_unmapped_area(part->master, len, offset, | 
 | 108 | 					       flags); | 
 | 109 | } | 
 | 110 |  | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 111 | static int part_read_oob(struct mtd_info *mtd, loff_t from, | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 112 | 		struct mtd_oob_ops *ops) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 | { | 
 | 114 | 	struct mtd_part *part = PART(mtd); | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 115 | 	int res; | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 116 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | 	if (from >= mtd->size) | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 118 | 		return -EINVAL; | 
| Vitaly Wool | 7014568 | 2006-11-03 18:20:38 +0300 | [diff] [blame] | 119 | 	if (ops->datbuf && from + ops->len > mtd->size) | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 120 | 		return -EINVAL; | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 121 | 	res = part->master->read_oob(part->master, from + part->offset, ops); | 
 | 122 |  | 
 | 123 | 	if (unlikely(res)) { | 
 | 124 | 		if (res == -EUCLEAN) | 
 | 125 | 			mtd->ecc_stats.corrected++; | 
 | 126 | 		if (res == -EBADMSG) | 
 | 127 | 			mtd->ecc_stats.failed++; | 
 | 128 | 	} | 
 | 129 | 	return res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | } | 
 | 131 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 132 | static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, | 
 | 133 | 		size_t len, size_t *retlen, u_char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 134 | { | 
 | 135 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 136 | 	return part->master->read_user_prot_reg(part->master, from, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | 					len, retlen, buf); | 
 | 138 | } | 
 | 139 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 140 | static int part_get_user_prot_info(struct mtd_info *mtd, | 
 | 141 | 		struct otp_info *buf, size_t len) | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 142 | { | 
 | 143 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 144 | 	return part->master->get_user_prot_info(part->master, buf, len); | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 145 | } | 
 | 146 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 147 | static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, | 
 | 148 | 		size_t len, size_t *retlen, u_char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 149 | { | 
 | 150 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 151 | 	return part->master->read_fact_prot_reg(part->master, from, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | 					len, retlen, buf); | 
 | 153 | } | 
 | 154 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 155 | static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, | 
 | 156 | 		size_t len) | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 157 | { | 
 | 158 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 159 | 	return part->master->get_fact_prot_info(part->master, buf, len); | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 160 | } | 
 | 161 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 162 | static int part_write(struct mtd_info *mtd, loff_t to, size_t len, | 
 | 163 | 		size_t *retlen, const u_char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | { | 
 | 165 | 	struct mtd_part *part = PART(mtd); | 
 | 166 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 167 | 		return -EROFS; | 
 | 168 | 	if (to >= mtd->size) | 
 | 169 | 		len = 0; | 
 | 170 | 	else if (to + len > mtd->size) | 
 | 171 | 		len = mtd->size - to; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 172 | 	return part->master->write(part->master, to + part->offset, | 
| Thomas Gleixner | 9223a45 | 2006-05-23 17:21:03 +0200 | [diff] [blame] | 173 | 				    len, retlen, buf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 174 | } | 
 | 175 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 176 | static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, | 
 | 177 | 		size_t *retlen, const u_char *buf) | 
| Richard Purdie | 388bbb0 | 2008-02-06 10:17:15 +0000 | [diff] [blame] | 178 | { | 
 | 179 | 	struct mtd_part *part = PART(mtd); | 
 | 180 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 181 | 		return -EROFS; | 
 | 182 | 	if (to >= mtd->size) | 
 | 183 | 		len = 0; | 
 | 184 | 	else if (to + len > mtd->size) | 
 | 185 | 		len = mtd->size - to; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 186 | 	return part->master->panic_write(part->master, to + part->offset, | 
| Richard Purdie | 388bbb0 | 2008-02-06 10:17:15 +0000 | [diff] [blame] | 187 | 				    len, retlen, buf); | 
 | 188 | } | 
 | 189 |  | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 190 | static int part_write_oob(struct mtd_info *mtd, loff_t to, | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 191 | 		struct mtd_oob_ops *ops) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | { | 
 | 193 | 	struct mtd_part *part = PART(mtd); | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 194 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 196 | 		return -EROFS; | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 197 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | 	if (to >= mtd->size) | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 199 | 		return -EINVAL; | 
| Vitaly Wool | 7014568 | 2006-11-03 18:20:38 +0300 | [diff] [blame] | 200 | 	if (ops->datbuf && to + ops->len > mtd->size) | 
| Thomas Gleixner | 8593fbc | 2006-05-29 03:26:58 +0200 | [diff] [blame] | 201 | 		return -EINVAL; | 
 | 202 | 	return part->master->write_oob(part->master, to + part->offset, ops); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 203 | } | 
 | 204 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 205 | static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, | 
 | 206 | 		size_t len, size_t *retlen, u_char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | { | 
 | 208 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 209 | 	return part->master->write_user_prot_reg(part->master, from, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 | 					len, retlen, buf); | 
 | 211 | } | 
 | 212 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 213 | static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | 
 | 214 | 		size_t len) | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 215 | { | 
 | 216 | 	struct mtd_part *part = PART(mtd); | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 217 | 	return part->master->lock_user_prot_reg(part->master, from, len); | 
| Nicolas Pitre | f77814d | 2005-02-08 17:11:19 +0000 | [diff] [blame] | 218 | } | 
 | 219 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 220 | static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, | 
 | 221 | 		unsigned long count, loff_t to, size_t *retlen) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | { | 
 | 223 | 	struct mtd_part *part = PART(mtd); | 
 | 224 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 225 | 		return -EROFS; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 226 | 	return part->master->writev(part->master, vecs, count, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | 					to + part->offset, retlen); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | } | 
 | 229 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 230 | static int part_erase(struct mtd_info *mtd, struct erase_info *instr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 231 | { | 
 | 232 | 	struct mtd_part *part = PART(mtd); | 
 | 233 | 	int ret; | 
 | 234 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 235 | 		return -EROFS; | 
 | 236 | 	if (instr->addr >= mtd->size) | 
 | 237 | 		return -EINVAL; | 
 | 238 | 	instr->addr += part->offset; | 
 | 239 | 	ret = part->master->erase(part->master, instr); | 
| Adrian Hunter | 74641d7 | 2007-03-08 12:20:12 +0200 | [diff] [blame] | 240 | 	if (ret) { | 
| Adrian Hunter | bb0eb21 | 2008-08-12 12:40:50 +0300 | [diff] [blame] | 241 | 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) | 
| Adrian Hunter | 74641d7 | 2007-03-08 12:20:12 +0200 | [diff] [blame] | 242 | 			instr->fail_addr -= part->offset; | 
 | 243 | 		instr->addr -= part->offset; | 
 | 244 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 245 | 	return ret; | 
 | 246 | } | 
 | 247 |  | 
 | 248 | void mtd_erase_callback(struct erase_info *instr) | 
 | 249 | { | 
 | 250 | 	if (instr->mtd->erase == part_erase) { | 
 | 251 | 		struct mtd_part *part = PART(instr->mtd); | 
 | 252 |  | 
| Adrian Hunter | bb0eb21 | 2008-08-12 12:40:50 +0300 | [diff] [blame] | 253 | 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 254 | 			instr->fail_addr -= part->offset; | 
 | 255 | 		instr->addr -= part->offset; | 
 | 256 | 	} | 
 | 257 | 	if (instr->callback) | 
 | 258 | 		instr->callback(instr); | 
 | 259 | } | 
 | 260 | EXPORT_SYMBOL_GPL(mtd_erase_callback); | 
 | 261 |  | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 262 | static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 | { | 
 | 264 | 	struct mtd_part *part = PART(mtd); | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 265 | 	if ((len + ofs) > mtd->size) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 266 | 		return -EINVAL; | 
 | 267 | 	return part->master->lock(part->master, ofs + part->offset, len); | 
 | 268 | } | 
 | 269 |  | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 270 | static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 271 | { | 
 | 272 | 	struct mtd_part *part = PART(mtd); | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 273 | 	if ((len + ofs) > mtd->size) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | 		return -EINVAL; | 
 | 275 | 	return part->master->unlock(part->master, ofs + part->offset, len); | 
 | 276 | } | 
 | 277 |  | 
| Richard Cochran | 9938424 | 2010-06-14 18:10:33 +0200 | [diff] [blame] | 278 | static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) | 
 | 279 | { | 
 | 280 | 	struct mtd_part *part = PART(mtd); | 
 | 281 | 	if ((len + ofs) > mtd->size) | 
 | 282 | 		return -EINVAL; | 
 | 283 | 	return part->master->is_locked(part->master, ofs + part->offset, len); | 
 | 284 | } | 
 | 285 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | static void part_sync(struct mtd_info *mtd) | 
 | 287 | { | 
 | 288 | 	struct mtd_part *part = PART(mtd); | 
 | 289 | 	part->master->sync(part->master); | 
 | 290 | } | 
 | 291 |  | 
 | 292 | static int part_suspend(struct mtd_info *mtd) | 
 | 293 | { | 
 | 294 | 	struct mtd_part *part = PART(mtd); | 
 | 295 | 	return part->master->suspend(part->master); | 
 | 296 | } | 
 | 297 |  | 
 | 298 | static void part_resume(struct mtd_info *mtd) | 
 | 299 | { | 
 | 300 | 	struct mtd_part *part = PART(mtd); | 
 | 301 | 	part->master->resume(part->master); | 
 | 302 | } | 
 | 303 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 304 | static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | { | 
 | 306 | 	struct mtd_part *part = PART(mtd); | 
 | 307 | 	if (ofs >= mtd->size) | 
 | 308 | 		return -EINVAL; | 
 | 309 | 	ofs += part->offset; | 
 | 310 | 	return part->master->block_isbad(part->master, ofs); | 
 | 311 | } | 
 | 312 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 313 | static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 | { | 
 | 315 | 	struct mtd_part *part = PART(mtd); | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 316 | 	int res; | 
 | 317 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 | 	if (!(mtd->flags & MTD_WRITEABLE)) | 
 | 319 | 		return -EROFS; | 
 | 320 | 	if (ofs >= mtd->size) | 
 | 321 | 		return -EINVAL; | 
 | 322 | 	ofs += part->offset; | 
| Thomas Gleixner | f1a28c0 | 2006-05-30 00:37:34 +0200 | [diff] [blame] | 323 | 	res = part->master->block_markbad(part->master, ofs); | 
 | 324 | 	if (!res) | 
 | 325 | 		mtd->ecc_stats.badblocks++; | 
 | 326 | 	return res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 327 | } | 
 | 328 |  | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 329 | /* | 
 | 330 |  * This function unregisters and destroy all slave MTD objects which are | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 331 |  * attached to the given master MTD object. | 
 | 332 |  */ | 
 | 333 |  | 
 | 334 | int del_mtd_partitions(struct mtd_info *master) | 
 | 335 | { | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 336 | 	struct mtd_part *slave, *next; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 |  | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 338 | 	list_for_each_entry_safe(slave, next, &mtd_partitions, list) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 339 | 		if (slave->master == master) { | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 340 | 			list_del(&slave->list); | 
| David Woodhouse | b90cf66 | 2009-04-05 08:23:44 -0700 | [diff] [blame] | 341 | 			del_mtd_device(&slave->mtd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 342 | 			kfree(slave); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 344 |  | 
 | 345 | 	return 0; | 
 | 346 | } | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 347 | EXPORT_SYMBOL(del_mtd_partitions); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 |  | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 349 | static struct mtd_part *add_one_partition(struct mtd_info *master, | 
 | 350 | 		const struct mtd_partition *part, int partno, | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 351 | 		uint64_t cur_offset) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 352 | { | 
 | 353 | 	struct mtd_part *slave; | 
 | 354 |  | 
 | 355 | 	/* allocate the partition structure */ | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 356 | 	slave = kzalloc(sizeof(*slave), GFP_KERNEL); | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 357 | 	if (!slave) { | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 358 | 		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 359 | 			master->name); | 
 | 360 | 		del_mtd_partitions(master); | 
 | 361 | 		return NULL; | 
 | 362 | 	} | 
 | 363 | 	list_add(&slave->list, &mtd_partitions); | 
 | 364 |  | 
 | 365 | 	/* set up the MTD object for this partition */ | 
 | 366 | 	slave->mtd.type = master->type; | 
 | 367 | 	slave->mtd.flags = master->flags & ~part->mask_flags; | 
 | 368 | 	slave->mtd.size = part->size; | 
 | 369 | 	slave->mtd.writesize = master->writesize; | 
 | 370 | 	slave->mtd.oobsize = master->oobsize; | 
 | 371 | 	slave->mtd.oobavail = master->oobavail; | 
 | 372 | 	slave->mtd.subpage_sft = master->subpage_sft; | 
 | 373 |  | 
 | 374 | 	slave->mtd.name = part->name; | 
 | 375 | 	slave->mtd.owner = master->owner; | 
| David Howells | 402d326 | 2009-02-12 10:40:00 +0000 | [diff] [blame] | 376 | 	slave->mtd.backing_dev_info = master->backing_dev_info; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 377 |  | 
| David Brownell | 1f24b5a | 2009-03-26 00:42:41 -0700 | [diff] [blame] | 378 | 	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone | 
 | 379 | 	 * to have the same data be in two different partitions. | 
 | 380 | 	 */ | 
 | 381 | 	slave->mtd.dev.parent = master->dev.parent; | 
 | 382 |  | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 383 | 	slave->mtd.read = part_read; | 
 | 384 | 	slave->mtd.write = part_write; | 
 | 385 |  | 
 | 386 | 	if (master->panic_write) | 
 | 387 | 		slave->mtd.panic_write = part_panic_write; | 
 | 388 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 389 | 	if (master->point && master->unpoint) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 390 | 		slave->mtd.point = part_point; | 
 | 391 | 		slave->mtd.unpoint = part_unpoint; | 
 | 392 | 	} | 
 | 393 |  | 
| David Howells | 402d326 | 2009-02-12 10:40:00 +0000 | [diff] [blame] | 394 | 	if (master->get_unmapped_area) | 
 | 395 | 		slave->mtd.get_unmapped_area = part_get_unmapped_area; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 396 | 	if (master->read_oob) | 
 | 397 | 		slave->mtd.read_oob = part_read_oob; | 
 | 398 | 	if (master->write_oob) | 
 | 399 | 		slave->mtd.write_oob = part_write_oob; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 400 | 	if (master->read_user_prot_reg) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 401 | 		slave->mtd.read_user_prot_reg = part_read_user_prot_reg; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 402 | 	if (master->read_fact_prot_reg) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 403 | 		slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 404 | 	if (master->write_user_prot_reg) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 405 | 		slave->mtd.write_user_prot_reg = part_write_user_prot_reg; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 406 | 	if (master->lock_user_prot_reg) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 407 | 		slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 408 | 	if (master->get_user_prot_info) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 409 | 		slave->mtd.get_user_prot_info = part_get_user_prot_info; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 410 | 	if (master->get_fact_prot_info) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 411 | 		slave->mtd.get_fact_prot_info = part_get_fact_prot_info; | 
 | 412 | 	if (master->sync) | 
 | 413 | 		slave->mtd.sync = part_sync; | 
| David Woodhouse | 4704a78 | 2009-04-05 07:56:23 -0700 | [diff] [blame] | 414 | 	if (!partno && !master->dev.class && master->suspend && master->resume) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 415 | 			slave->mtd.suspend = part_suspend; | 
 | 416 | 			slave->mtd.resume = part_resume; | 
 | 417 | 	} | 
 | 418 | 	if (master->writev) | 
 | 419 | 		slave->mtd.writev = part_writev; | 
 | 420 | 	if (master->lock) | 
 | 421 | 		slave->mtd.lock = part_lock; | 
 | 422 | 	if (master->unlock) | 
 | 423 | 		slave->mtd.unlock = part_unlock; | 
| Richard Cochran | 9938424 | 2010-06-14 18:10:33 +0200 | [diff] [blame] | 424 | 	if (master->is_locked) | 
 | 425 | 		slave->mtd.is_locked = part_is_locked; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 426 | 	if (master->block_isbad) | 
 | 427 | 		slave->mtd.block_isbad = part_block_isbad; | 
 | 428 | 	if (master->block_markbad) | 
 | 429 | 		slave->mtd.block_markbad = part_block_markbad; | 
 | 430 | 	slave->mtd.erase = part_erase; | 
 | 431 | 	slave->master = master; | 
 | 432 | 	slave->offset = part->offset; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 433 |  | 
 | 434 | 	if (slave->offset == MTDPART_OFS_APPEND) | 
 | 435 | 		slave->offset = cur_offset; | 
 | 436 | 	if (slave->offset == MTDPART_OFS_NXTBLK) { | 
 | 437 | 		slave->offset = cur_offset; | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 438 | 		if (mtd_mod_by_eb(cur_offset, master) != 0) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 439 | 			/* Round up to next erasesize */ | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 440 | 			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 441 | 			printk(KERN_NOTICE "Moving partition %d: " | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 442 | 			       "0x%012llx -> 0x%012llx\n", partno, | 
 | 443 | 			       (unsigned long long)cur_offset, (unsigned long long)slave->offset); | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 444 | 		} | 
 | 445 | 	} | 
 | 446 | 	if (slave->mtd.size == MTDPART_SIZ_FULL) | 
 | 447 | 		slave->mtd.size = master->size - slave->offset; | 
 | 448 |  | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 449 | 	printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, | 
 | 450 | 		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 451 |  | 
 | 452 | 	/* let's do some sanity checks */ | 
 | 453 | 	if (slave->offset >= master->size) { | 
| Atsushi Nemoto | f636ffb | 2008-07-19 01:01:22 +0900 | [diff] [blame] | 454 | 		/* let's register it anyway to preserve ordering */ | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 455 | 		slave->offset = 0; | 
 | 456 | 		slave->mtd.size = 0; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 457 | 		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 458 | 			part->name); | 
| Atsushi Nemoto | f636ffb | 2008-07-19 01:01:22 +0900 | [diff] [blame] | 459 | 		goto out_register; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 460 | 	} | 
 | 461 | 	if (slave->offset + slave->mtd.size > master->size) { | 
 | 462 | 		slave->mtd.size = master->size - slave->offset; | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 463 | 		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", | 
 | 464 | 			part->name, master->name, (unsigned long long)slave->mtd.size); | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 465 | 	} | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 466 | 	if (master->numeraseregions > 1) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 467 | 		/* Deal with variable erase size stuff */ | 
| Atsushi Nemoto | 6910c13 | 2008-07-19 01:00:57 +0900 | [diff] [blame] | 468 | 		int i, max = master->numeraseregions; | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 469 | 		u64 end = slave->offset + slave->mtd.size; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 470 | 		struct mtd_erase_region_info *regions = master->eraseregions; | 
 | 471 |  | 
| Atsushi Nemoto | 6910c13 | 2008-07-19 01:00:57 +0900 | [diff] [blame] | 472 | 		/* Find the first erase regions which is part of this | 
 | 473 | 		 * partition. */ | 
 | 474 | 		for (i = 0; i < max && regions[i].offset <= slave->offset; i++) | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 475 | 			; | 
| Atsushi Nemoto | 6910c13 | 2008-07-19 01:00:57 +0900 | [diff] [blame] | 476 | 		/* The loop searched for the region _behind_ the first one */ | 
| Roel Kluin | a57ca04 | 2009-09-18 12:51:50 -0700 | [diff] [blame] | 477 | 		if (i > 0) | 
 | 478 | 			i--; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 479 |  | 
| Atsushi Nemoto | 6910c13 | 2008-07-19 01:00:57 +0900 | [diff] [blame] | 480 | 		/* Pick biggest erasesize */ | 
 | 481 | 		for (; i < max && regions[i].offset < end; i++) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 482 | 			if (slave->mtd.erasesize < regions[i].erasesize) { | 
 | 483 | 				slave->mtd.erasesize = regions[i].erasesize; | 
 | 484 | 			} | 
 | 485 | 		} | 
| Atsushi Nemoto | 6910c13 | 2008-07-19 01:00:57 +0900 | [diff] [blame] | 486 | 		BUG_ON(slave->mtd.erasesize == 0); | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 487 | 	} else { | 
 | 488 | 		/* Single erase size */ | 
 | 489 | 		slave->mtd.erasesize = master->erasesize; | 
 | 490 | 	} | 
 | 491 |  | 
 | 492 | 	if ((slave->mtd.flags & MTD_WRITEABLE) && | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 493 | 	    mtd_mod_by_eb(slave->offset, &slave->mtd)) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 494 | 		/* Doesn't start on a boundary of major erase size */ | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 495 | 		/* FIXME: Let it be writable if it is on a boundary of | 
 | 496 | 		 * _minor_ erase size though */ | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 497 | 		slave->mtd.flags &= ~MTD_WRITEABLE; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 498 | 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 499 | 			part->name); | 
 | 500 | 	} | 
 | 501 | 	if ((slave->mtd.flags & MTD_WRITEABLE) && | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 502 | 	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 503 | 		slave->mtd.flags &= ~MTD_WRITEABLE; | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 504 | 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 505 | 			part->name); | 
 | 506 | 	} | 
 | 507 |  | 
 | 508 | 	slave->mtd.ecclayout = master->ecclayout; | 
 | 509 | 	if (master->block_isbad) { | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 510 | 		uint64_t offs = 0; | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 511 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 512 | 		while (offs < slave->mtd.size) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 513 | 			if (master->block_isbad(master, | 
 | 514 | 						offs + slave->offset)) | 
 | 515 | 				slave->mtd.ecc_stats.badblocks++; | 
 | 516 | 			offs += slave->mtd.erasesize; | 
 | 517 | 		} | 
 | 518 | 	} | 
 | 519 |  | 
| Atsushi Nemoto | f636ffb | 2008-07-19 01:01:22 +0900 | [diff] [blame] | 520 | out_register: | 
| David Woodhouse | b90cf66 | 2009-04-05 08:23:44 -0700 | [diff] [blame] | 521 | 	/* register our partition */ | 
 | 522 | 	add_mtd_device(&slave->mtd); | 
 | 523 |  | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 524 | 	return slave; | 
 | 525 | } | 
 | 526 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 | /* | 
 | 528 |  * This function, given a master MTD object and a partition table, creates | 
 | 529 |  * and registers slave MTD objects which are bound to the master according to | 
 | 530 |  * the partition definitions. | 
| David Brownell | 1f24b5a | 2009-03-26 00:42:41 -0700 | [diff] [blame] | 531 |  * | 
 | 532 |  * We don't register the master, or expect the caller to have done so, | 
 | 533 |  * for reasons of data integrity. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 534 |  */ | 
 | 535 |  | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 536 | int add_mtd_partitions(struct mtd_info *master, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 537 | 		       const struct mtd_partition *parts, | 
 | 538 | 		       int nbparts) | 
 | 539 | { | 
 | 540 | 	struct mtd_part *slave; | 
| Adrian Hunter | 69423d9 | 2008-12-10 13:37:21 +0000 | [diff] [blame] | 541 | 	uint64_t cur_offset = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 542 | 	int i; | 
 | 543 |  | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 544 | 	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 545 |  | 
 | 546 | 	for (i = 0; i < nbparts; i++) { | 
| Atsushi Nemoto | 7788ba7 | 2008-07-19 01:00:18 +0900 | [diff] [blame] | 547 | 		slave = add_one_partition(master, parts + i, i, cur_offset); | 
 | 548 | 		if (!slave) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 | 			return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 550 | 		cur_offset = slave->offset + slave->mtd.size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 551 | 	} | 
 | 552 |  | 
 | 553 | 	return 0; | 
 | 554 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 555 | EXPORT_SYMBOL(add_mtd_partitions); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 556 |  | 
 | 557 | static DEFINE_SPINLOCK(part_parser_lock); | 
 | 558 | static LIST_HEAD(part_parsers); | 
 | 559 |  | 
 | 560 | static struct mtd_part_parser *get_partition_parser(const char *name) | 
 | 561 | { | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 562 | 	struct mtd_part_parser *p, *ret = NULL; | 
 | 563 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | 	spin_lock(&part_parser_lock); | 
 | 565 |  | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 566 | 	list_for_each_entry(p, &part_parsers, list) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 567 | 		if (!strcmp(p->name, name) && try_module_get(p->owner)) { | 
 | 568 | 			ret = p; | 
 | 569 | 			break; | 
 | 570 | 		} | 
| Chris Malley | 71a928c | 2008-05-19 20:11:50 +0100 | [diff] [blame] | 571 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 | 	spin_unlock(&part_parser_lock); | 
 | 573 |  | 
 | 574 | 	return ret; | 
 | 575 | } | 
 | 576 |  | 
 | 577 | int register_mtd_parser(struct mtd_part_parser *p) | 
 | 578 | { | 
 | 579 | 	spin_lock(&part_parser_lock); | 
 | 580 | 	list_add(&p->list, &part_parsers); | 
 | 581 | 	spin_unlock(&part_parser_lock); | 
 | 582 |  | 
 | 583 | 	return 0; | 
 | 584 | } | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 585 | EXPORT_SYMBOL_GPL(register_mtd_parser); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 |  | 
 | 587 | int deregister_mtd_parser(struct mtd_part_parser *p) | 
 | 588 | { | 
 | 589 | 	spin_lock(&part_parser_lock); | 
 | 590 | 	list_del(&p->list); | 
 | 591 | 	spin_unlock(&part_parser_lock); | 
 | 592 | 	return 0; | 
 | 593 | } | 
| Atsushi Nemoto | b33a288 | 2008-07-19 01:00:33 +0900 | [diff] [blame] | 594 | EXPORT_SYMBOL_GPL(deregister_mtd_parser); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 |  | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 596 | int parse_mtd_partitions(struct mtd_info *master, const char **types, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 597 | 			 struct mtd_partition **pparts, unsigned long origin) | 
 | 598 | { | 
 | 599 | 	struct mtd_part_parser *parser; | 
 | 600 | 	int ret = 0; | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 601 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 602 | 	for ( ; ret <= 0 && *types; types++) { | 
 | 603 | 		parser = get_partition_parser(*types); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 604 | 		if (!parser && !request_module("%s", *types)) | 
 | 605 | 				parser = get_partition_parser(*types); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | 		if (!parser) { | 
 | 607 | 			printk(KERN_NOTICE "%s partition parsing not available\n", | 
 | 608 | 			       *types); | 
 | 609 | 			continue; | 
 | 610 | 		} | 
 | 611 | 		ret = (*parser->parse_fn)(master, pparts, origin); | 
 | 612 | 		if (ret > 0) { | 
| Thomas Gleixner | 97894cd | 2005-11-07 11:15:26 +0000 | [diff] [blame] | 613 | 			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 614 | 			       ret, parser->name, master->name); | 
 | 615 | 		} | 
 | 616 | 		put_partition_parser(parser); | 
 | 617 | 	} | 
 | 618 | 	return ret; | 
 | 619 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 620 | EXPORT_SYMBOL_GPL(parse_mtd_partitions); |