diff options
Diffstat (limited to 'drivers/mtd/mtdpart.c')
| -rw-r--r-- | drivers/mtd/mtdpart.c | 353 | 
1 files changed, 195 insertions, 158 deletions
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 79e3689f1e1..1ca9aec141f 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -31,6 +31,8 @@  #include <linux/mtd/partitions.h>  #include <linux/err.h> +#include "mtdcore.h" +  /* Our partition linked list */  static LIST_HEAD(mtd_partitions);  static DEFINE_MUTEX(mtd_partitions_mutex); @@ -63,19 +65,14 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,  	int res;  	stats = part->master->ecc_stats; - -	if (from >= mtd->size) -		len = 0; -	else if (from + len > mtd->size) -		len = mtd->size - from; -	res = part->master->read(part->master, from + part->offset, -				   len, retlen, buf); -	if (unlikely(res)) { -		if (res == -EUCLEAN) -			mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; -		if (res == -EBADMSG) -			mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; -	} +	res = part->master->_read(part->master, from + part->offset, len, +				  retlen, buf); +	if (unlikely(mtd_is_eccerr(res))) +		mtd->ecc_stats.failed += +			part->master->ecc_stats.failed - stats.failed; +	else +		mtd->ecc_stats.corrected += +			part->master->ecc_stats.corrected - stats.corrected;  	return res;  } @@ -83,19 +80,16 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,  		size_t *retlen, void **virt, resource_size_t *phys)  {  	struct mtd_part *part = PART(mtd); -	if (from >= mtd->size) -		len = 0; -	else if (from + len > mtd->size) -		len = mtd->size - from; -	return part->master->point (part->master, from + part->offset, -				    len, retlen, virt, phys); + +	return part->master->_point(part->master, from + part->offset, len, +				    retlen, virt, phys);  } -static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)  {  	struct mtd_part *part = PART(mtd); -	part->master->unpoint(part->master, from + part->offset, len); +	return part->master->_unpoint(part->master, from + part->offset, len);  }  static unsigned long part_get_unmapped_area(struct mtd_info *mtd, @@ -106,8 +100,8 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,  	struct mtd_part *part = PART(mtd);  	offset += part->offset; -	return part->master->get_unmapped_area(part->master, len, offset, -					       flags); +	return part->master->_get_unmapped_area(part->master, len, offset, +						flags);  }  static int part_read_oob(struct mtd_info *mtd, loff_t from, @@ -120,12 +114,29 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,  		return -EINVAL;  	if (ops->datbuf && from + ops->len > mtd->size)  		return -EINVAL; -	res = part->master->read_oob(part->master, from + part->offset, ops); +	/* +	 * If OOB is also requested, make sure that we do not read past the end +	 * of this partition. +	 */ +	if (ops->oobbuf) { +		size_t len, pages; + +		if (ops->mode == MTD_OPS_AUTO_OOB) +			len = mtd->oobavail; +		else +			len = mtd->oobsize; +		pages = mtd_div_by_ws(mtd->size, mtd); +		pages -= mtd_div_by_ws(from, mtd); +		if (ops->ooboffs + ops->ooblen > pages * len) +			return -EINVAL; +	} + +	res = part->master->_read_oob(part->master, from + part->offset, ops);  	if (unlikely(res)) { -		if (res == -EUCLEAN) +		if (mtd_is_bitflip(res))  			mtd->ecc_stats.corrected++; -		if (res == -EBADMSG) +		if (mtd_is_eccerr(res))  			mtd->ecc_stats.failed++;  	}  	return res; @@ -135,58 +146,48 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->read_user_prot_reg(part->master, from, -					len, retlen, buf); +	return part->master->_read_user_prot_reg(part->master, from, len, +						 retlen, buf);  } -static int part_get_user_prot_info(struct mtd_info *mtd, -		struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, +				   size_t *retlen, struct otp_info *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->get_user_prot_info(part->master, buf, len); +	return part->master->_get_user_prot_info(part->master, len, retlen, +						 buf);  }  static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->read_fact_prot_reg(part->master, from, -					len, retlen, buf); +	return part->master->_read_fact_prot_reg(part->master, from, len, +						 retlen, buf);  } -static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, -		size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, +				   size_t *retlen, struct otp_info *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->get_fact_prot_info(part->master, buf, len); +	return part->master->_get_fact_prot_info(part->master, len, retlen, +						 buf);  }  static int part_write(struct mtd_info *mtd, loff_t to, size_t len,  		size_t *retlen, const u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (to >= mtd->size) -		len = 0; -	else if (to + len > mtd->size) -		len = mtd->size - to; -	return part->master->write(part->master, to + part->offset, -				    len, retlen, buf); +	return part->master->_write(part->master, to + part->offset, len, +				    retlen, buf);  }  static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,  		size_t *retlen, const u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (to >= mtd->size) -		len = 0; -	else if (to + len > mtd->size) -		len = mtd->size - to; -	return part->master->panic_write(part->master, to + part->offset, -				    len, retlen, buf); +	return part->master->_panic_write(part->master, to + part->offset, len, +					  retlen, buf);  }  static int part_write_oob(struct mtd_info *mtd, loff_t to, @@ -194,51 +195,43 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -  	if (to >= mtd->size)  		return -EINVAL;  	if (ops->datbuf && to + ops->len > mtd->size)  		return -EINVAL; -	return part->master->write_oob(part->master, to + part->offset, ops); +	return part->master->_write_oob(part->master, to + part->offset, ops);  }  static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->write_user_prot_reg(part->master, from, -					len, retlen, buf); +	return part->master->_write_user_prot_reg(part->master, from, len, +						  retlen, buf);  }  static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len)  {  	struct mtd_part *part = PART(mtd); -	return part->master->lock_user_prot_reg(part->master, from, len); +	return part->master->_lock_user_prot_reg(part->master, from, len);  }  static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,  		unsigned long count, loff_t to, size_t *retlen)  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	return part->master->writev(part->master, vecs, count, -					to + part->offset, retlen); +	return part->master->_writev(part->master, vecs, count, +				     to + part->offset, retlen);  }  static int part_erase(struct mtd_info *mtd, struct erase_info *instr)  {  	struct mtd_part *part = PART(mtd);  	int ret; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (instr->addr >= mtd->size) -		return -EINVAL; +  	instr->addr += part->offset; -	ret = part->master->erase(part->master, instr); +	ret = part->master->_erase(part->master, instr);  	if (ret) {  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)  			instr->fail_addr -= part->offset; @@ -249,7 +242,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)  void mtd_erase_callback(struct erase_info *instr)  { -	if (instr->mtd->erase == part_erase) { +	if (instr->mtd->_erase == part_erase) {  		struct mtd_part *part = PART(instr->mtd);  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) @@ -264,52 +257,44 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);  static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  {  	struct mtd_part *part = PART(mtd); -	if ((len + ofs) > mtd->size) -		return -EINVAL; -	return part->master->lock(part->master, ofs + part->offset, len); +	return part->master->_lock(part->master, ofs + part->offset, len);  }  static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  {  	struct mtd_part *part = PART(mtd); -	if ((len + ofs) > mtd->size) -		return -EINVAL; -	return part->master->unlock(part->master, ofs + part->offset, len); +	return part->master->_unlock(part->master, ofs + part->offset, len);  }  static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)  {  	struct mtd_part *part = PART(mtd); -	if ((len + ofs) > mtd->size) -		return -EINVAL; -	return part->master->is_locked(part->master, ofs + part->offset, len); +	return part->master->_is_locked(part->master, ofs + part->offset, len);  }  static void part_sync(struct mtd_info *mtd)  {  	struct mtd_part *part = PART(mtd); -	part->master->sync(part->master); +	part->master->_sync(part->master);  }  static int part_suspend(struct mtd_info *mtd)  {  	struct mtd_part *part = PART(mtd); -	return part->master->suspend(part->master); +	return part->master->_suspend(part->master);  }  static void part_resume(struct mtd_info *mtd)  {  	struct mtd_part *part = PART(mtd); -	part->master->resume(part->master); +	part->master->_resume(part->master);  }  static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)  {  	struct mtd_part *part = PART(mtd); -	if (ofs >= mtd->size) -		return -EINVAL;  	ofs += part->offset; -	return part->master->block_isbad(part->master, ofs); +	return part->master->_block_isbad(part->master, ofs);  }  static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) @@ -317,12 +302,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)  	struct mtd_part *part = PART(mtd);  	int res; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (ofs >= mtd->size) -		return -EINVAL;  	ofs += part->offset; -	res = part->master->block_markbad(part->master, ofs); +	res = part->master->_block_markbad(part->master, ofs);  	if (!res)  		mtd->ecc_stats.badblocks++;  	return res; @@ -359,7 +340,6 @@ int del_mtd_partitions(struct mtd_info *master)  	return err;  } -EXPORT_SYMBOL(del_mtd_partitions);  static struct mtd_part *allocate_partition(struct mtd_info *master,  			const struct mtd_partition *part, int partno, @@ -384,6 +364,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,  	slave->mtd.flags = master->flags & ~part->mask_flags;  	slave->mtd.size = part->size;  	slave->mtd.writesize = master->writesize; +	slave->mtd.writebufsize = master->writebufsize;  	slave->mtd.oobsize = master->oobsize;  	slave->mtd.oobavail = master->oobavail;  	slave->mtd.subpage_sft = master->subpage_sft; @@ -397,54 +378,55 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,  	 */  	slave->mtd.dev.parent = master->dev.parent; -	slave->mtd.read = part_read; -	slave->mtd.write = part_write; +	slave->mtd._read = part_read; +	slave->mtd._write = part_write; -	if (master->panic_write) -		slave->mtd.panic_write = part_panic_write; +	if (master->_panic_write) +		slave->mtd._panic_write = part_panic_write; -	if (master->point && master->unpoint) { -		slave->mtd.point = part_point; -		slave->mtd.unpoint = part_unpoint; +	if (master->_point && master->_unpoint) { +		slave->mtd._point = part_point; +		slave->mtd._unpoint = part_unpoint;  	} -	if (master->get_unmapped_area) -		slave->mtd.get_unmapped_area = part_get_unmapped_area; -	if (master->read_oob) -		slave->mtd.read_oob = part_read_oob; -	if (master->write_oob) -		slave->mtd.write_oob = part_write_oob; -	if (master->read_user_prot_reg) -		slave->mtd.read_user_prot_reg = part_read_user_prot_reg; -	if (master->read_fact_prot_reg) -		slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; -	if (master->write_user_prot_reg) -		slave->mtd.write_user_prot_reg = part_write_user_prot_reg; -	if (master->lock_user_prot_reg) -		slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; -	if (master->get_user_prot_info) -		slave->mtd.get_user_prot_info = part_get_user_prot_info; -	if (master->get_fact_prot_info) -		slave->mtd.get_fact_prot_info = part_get_fact_prot_info; -	if (master->sync) -		slave->mtd.sync = part_sync; -	if (!partno && !master->dev.class && master->suspend && master->resume) { -			slave->mtd.suspend = part_suspend; -			slave->mtd.resume = part_resume; +	if (master->_get_unmapped_area) +		slave->mtd._get_unmapped_area = part_get_unmapped_area; +	if (master->_read_oob) +		slave->mtd._read_oob = part_read_oob; +	if (master->_write_oob) +		slave->mtd._write_oob = part_write_oob; +	if (master->_read_user_prot_reg) +		slave->mtd._read_user_prot_reg = part_read_user_prot_reg; +	if (master->_read_fact_prot_reg) +		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; +	if (master->_write_user_prot_reg) +		slave->mtd._write_user_prot_reg = part_write_user_prot_reg; +	if (master->_lock_user_prot_reg) +		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; +	if (master->_get_user_prot_info) +		slave->mtd._get_user_prot_info = part_get_user_prot_info; +	if (master->_get_fact_prot_info) +		slave->mtd._get_fact_prot_info = part_get_fact_prot_info; +	if (master->_sync) +		slave->mtd._sync = part_sync; +	if (!partno && !master->dev.class && master->_suspend && +	    master->_resume) { +			slave->mtd._suspend = part_suspend; +			slave->mtd._resume = part_resume;  	} -	if (master->writev) -		slave->mtd.writev = part_writev; -	if (master->lock) -		slave->mtd.lock = part_lock; -	if (master->unlock) -		slave->mtd.unlock = part_unlock; -	if (master->is_locked) -		slave->mtd.is_locked = part_is_locked; -	if (master->block_isbad) -		slave->mtd.block_isbad = part_block_isbad; -	if (master->block_markbad) -		slave->mtd.block_markbad = part_block_markbad; -	slave->mtd.erase = part_erase; +	if (master->_writev) +		slave->mtd._writev = part_writev; +	if (master->_lock) +		slave->mtd._lock = part_lock; +	if (master->_unlock) +		slave->mtd._unlock = part_unlock; +	if (master->_is_locked) +		slave->mtd._is_locked = part_is_locked; +	if (master->_block_isbad) +		slave->mtd._block_isbad = part_block_isbad; +	if (master->_block_markbad) +		slave->mtd._block_markbad = part_block_markbad; +	slave->mtd._erase = part_erase;  	slave->master = master;  	slave->offset = part->offset; @@ -460,6 +442,19 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,  			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);  		}  	} +	if (slave->offset == MTDPART_OFS_RETAIN) { +		slave->offset = cur_offset; +		if (master->size - slave->offset >= slave->mtd.size) { +			slave->mtd.size = master->size - slave->offset +							- slave->mtd.size; +		} else { +			printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", +				part->name, master->size - slave->offset, +				slave->mtd.size); +			/* register to preserve ordering */ +			goto out_register; +		} +	}  	if (slave->mtd.size == MTDPART_SIZ_FULL)  		slave->mtd.size = master->size - slave->offset; @@ -523,12 +518,15 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,  	}  	slave->mtd.ecclayout = master->ecclayout; -	if (master->block_isbad) { +	slave->mtd.ecc_step_size = master->ecc_step_size; +	slave->mtd.ecc_strength = master->ecc_strength; +	slave->mtd.bitflip_threshold = master->bitflip_threshold; + +	if (master->_block_isbad) {  		uint64_t offs = 0;  		while (offs < slave->mtd.size) { -			if (master->block_isbad(master, -						offs + slave->offset)) +			if (mtd_block_isbad(master, offs + slave->offset))  				slave->mtd.ecc_stats.badblocks++;  			offs += slave->mtd.erasesize;  		} @@ -538,7 +536,7 @@ out_register:  	return slave;  } -int mtd_add_partition(struct mtd_info *master, char *name, +int mtd_add_partition(struct mtd_info *master, const char *name,  		      long long offset, long long length)  {  	struct mtd_partition part; @@ -653,7 +651,6 @@ int add_mtd_partitions(struct mtd_info *master,  	return 0;  } -EXPORT_SYMBOL(add_mtd_partitions);  static DEFINE_SPINLOCK(part_parser_lock);  static LIST_HEAD(part_parsers); @@ -675,64 +672,104 @@ static struct mtd_part_parser *get_partition_parser(const char *name)  	return ret;  } -int register_mtd_parser(struct mtd_part_parser *p) +#define put_partition_parser(p) do { module_put((p)->owner); } while (0) + +void register_mtd_parser(struct mtd_part_parser *p)  {  	spin_lock(&part_parser_lock);  	list_add(&p->list, &part_parsers);  	spin_unlock(&part_parser_lock); - -	return 0;  }  EXPORT_SYMBOL_GPL(register_mtd_parser); -int deregister_mtd_parser(struct mtd_part_parser *p) +void deregister_mtd_parser(struct mtd_part_parser *p)  {  	spin_lock(&part_parser_lock);  	list_del(&p->list);  	spin_unlock(&part_parser_lock); -	return 0;  }  EXPORT_SYMBOL_GPL(deregister_mtd_parser); -int parse_mtd_partitions(struct mtd_info *master, const char **types, -			 struct mtd_partition **pparts, unsigned long origin) +/* + * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you + * are changing this array! + */ +static const char * const default_mtd_part_types[] = { +	"cmdlinepart", +	"ofpart", +	NULL +}; + +/** + * parse_mtd_partitions - parse MTD partitions + * @master: the master partition (describes whole MTD device) + * @types: names of partition parsers to try or %NULL + * @pparts: array of partitions found is returned here + * @data: MTD partition parser-specific data + * + * This function tries to find partition on MTD device @master. It uses MTD + * partition parsers, specified in @types. However, if @types is %NULL, then + * the default list of parsers is used. The default list contains only the + * "cmdlinepart" and "ofpart" parsers ATM. + * Note: If there are more then one parser in @types, the kernel only takes the + * partitions parsed out by the first parser. + * + * This function may return: + * o a negative error code in case of failure + * o zero if no partitions were found + * o a positive number of found partitions, in which case on exit @pparts will + *   point to an array containing this number of &struct mtd_info objects. + */ +int parse_mtd_partitions(struct mtd_info *master, const char *const *types, +			 struct mtd_partition **pparts, +			 struct mtd_part_parser_data *data)  {  	struct mtd_part_parser *parser;  	int ret = 0; +	if (!types) +		types = default_mtd_part_types; +  	for ( ; ret <= 0 && *types; types++) {  		parser = get_partition_parser(*types);  		if (!parser && !request_module("%s", *types)) -				parser = get_partition_parser(*types); -		if (!parser) { -			printk(KERN_NOTICE "%s partition parsing not available\n", -			       *types); +			parser = get_partition_parser(*types); +		if (!parser)  			continue; -		} -		ret = (*parser->parse_fn)(master, pparts, origin); +		ret = (*parser->parse_fn)(master, pparts, data); +		put_partition_parser(parser);  		if (ret > 0) {  			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",  			       ret, parser->name, master->name); +			break;  		} -		put_partition_parser(parser);  	}  	return ret;  } -EXPORT_SYMBOL_GPL(parse_mtd_partitions); -int mtd_is_master(struct mtd_info *mtd) +int mtd_is_partition(const struct mtd_info *mtd)  {  	struct mtd_part *part; -	int nopart = 0; +	int ispart = 0;  	mutex_lock(&mtd_partitions_mutex);  	list_for_each_entry(part, &mtd_partitions, list)  		if (&part->mtd == mtd) { -			nopart = 1; +			ispart = 1;  			break;  		}  	mutex_unlock(&mtd_partitions_mutex); -	return nopart; +	return ispart; +} +EXPORT_SYMBOL_GPL(mtd_is_partition); + +/* Returns the size of the entire flash chip */ +uint64_t mtd_get_device_size(const struct mtd_info *mtd) +{ +	if (!mtd_is_partition(mtd)) +		return mtd->size; + +	return PART(mtd)->master->size;  } -EXPORT_SYMBOL_GPL(mtd_is_master); +EXPORT_SYMBOL_GPL(mtd_get_device_size);  | 
