This MTD part infrastructure will be used by the upcoming UBI support. Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Stefan Roese <sr@denx.de>master
parent
9b827cf172
commit
e29c22f5ab
@ -0,0 +1,146 @@ |
||||
/*
|
||||
* Core registration and callback routines for MTD |
||||
* drivers and users. |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
*/ |
||||
|
||||
#include <linux/mtd/mtd.h> |
||||
#include <linux/mtd/compat.h> |
||||
#include <ubi_uboot.h> |
||||
|
||||
struct mtd_info *mtd_table[MAX_MTD_DEVICES]; |
||||
|
||||
int add_mtd_device(struct mtd_info *mtd) |
||||
{ |
||||
int i; |
||||
|
||||
BUG_ON(mtd->writesize == 0); |
||||
|
||||
for (i = 0; i < MAX_MTD_DEVICES; i++) |
||||
if (!mtd_table[i]) { |
||||
mtd_table[i] = mtd; |
||||
mtd->index = i; |
||||
mtd->usecount = 0; |
||||
|
||||
printf("mtd: Giving out device %d to %s\n", |
||||
i, mtd->name); |
||||
/* No need to get a refcount on the module containing
|
||||
the notifier, since we hold the mtd_table_mutex */ |
||||
|
||||
/* We _know_ we aren't being removed, because
|
||||
our caller is still holding us here. So none |
||||
of this try_ nonsense, and no bitching about it |
||||
either. :) */ |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
/**
|
||||
* del_mtd_device - unregister an MTD device |
||||
* @mtd: pointer to MTD device info structure |
||||
* |
||||
* Remove a device from the list of MTD devices present in the system, |
||||
* and notify each currently active MTD 'user' of its departure. |
||||
* Returns zero on success or 1 on failure, which currently will happen |
||||
* if the requested device does not appear to be present in the list. |
||||
*/ |
||||
int del_mtd_device(struct mtd_info *mtd) |
||||
{ |
||||
int ret; |
||||
|
||||
if (mtd_table[mtd->index] != mtd) { |
||||
ret = -ENODEV; |
||||
} else if (mtd->usecount) { |
||||
printk(KERN_NOTICE "Removing MTD device #%d (%s)" |
||||
" with use count %d\n", |
||||
mtd->index, mtd->name, mtd->usecount); |
||||
ret = -EBUSY; |
||||
} else { |
||||
/* No need to get a refcount on the module containing
|
||||
* the notifier, since we hold the mtd_table_mutex */ |
||||
mtd_table[mtd->index] = NULL; |
||||
|
||||
ret = 0; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* get_mtd_device - obtain a validated handle for an MTD device |
||||
* @mtd: last known address of the required MTD device |
||||
* @num: internal device number of the required MTD device |
||||
* |
||||
* Given a number and NULL address, return the num'th entry in the device |
||||
* table, if any. Given an address and num == -1, search the device table |
||||
* for a device with that address and return if it's still present. Given |
||||
* both, return the num'th driver only if its address matches. Return |
||||
* error code if not. |
||||
*/ |
||||
struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) |
||||
{ |
||||
struct mtd_info *ret = NULL; |
||||
int i, err = -ENODEV; |
||||
|
||||
if (num == -1) { |
||||
for (i = 0; i < MAX_MTD_DEVICES; i++) |
||||
if (mtd_table[i] == mtd) |
||||
ret = mtd_table[i]; |
||||
} else if (num < MAX_MTD_DEVICES) { |
||||
ret = mtd_table[num]; |
||||
if (mtd && mtd != ret) |
||||
ret = NULL; |
||||
} |
||||
|
||||
if (!ret) |
||||
goto out_unlock; |
||||
|
||||
ret->usecount++; |
||||
return ret; |
||||
|
||||
out_unlock: |
||||
return ERR_PTR(err); |
||||
} |
||||
|
||||
/**
|
||||
* get_mtd_device_nm - obtain a validated handle for an MTD device by |
||||
* device name |
||||
* @name: MTD device name to open |
||||
* |
||||
* This function returns MTD device description structure in case of |
||||
* success and an error code in case of failure. |
||||
*/ |
||||
struct mtd_info *get_mtd_device_nm(const char *name) |
||||
{ |
||||
int i, err = -ENODEV; |
||||
struct mtd_info *mtd = NULL; |
||||
|
||||
for (i = 0; i < MAX_MTD_DEVICES; i++) { |
||||
if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { |
||||
mtd = mtd_table[i]; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!mtd) |
||||
goto out_unlock; |
||||
|
||||
mtd->usecount++; |
||||
return mtd; |
||||
|
||||
out_unlock: |
||||
return ERR_PTR(err); |
||||
} |
||||
|
||||
void put_mtd_device(struct mtd_info *mtd) |
||||
{ |
||||
int c; |
||||
|
||||
c = --mtd->usecount; |
||||
BUG_ON(c < 0); |
||||
} |
@ -0,0 +1,532 @@ |
||||
/*
|
||||
* Simple MTD partitioning layer |
||||
* |
||||
* (C) 2000 Nicolas Pitre <nico@cam.org> |
||||
* |
||||
* This code is GPL |
||||
* |
||||
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de> |
||||
* added support for read_oob, write_oob |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <malloc.h> |
||||
#include <asm/errno.h> |
||||
|
||||
#include <linux/types.h> |
||||
#include <linux/list.h> |
||||
#include <linux/mtd/mtd.h> |
||||
#include <linux/mtd/partitions.h> |
||||
#include <linux/mtd/compat.h> |
||||
|
||||
/* Our partition linked list */ |
||||
static LIST_HEAD(mtd_partitions); |
||||
|
||||
/* Our partition node structure */ |
||||
struct mtd_part { |
||||
struct mtd_info mtd; |
||||
struct mtd_info *master; |
||||
u_int32_t offset; |
||||
int index; |
||||
struct list_head list; |
||||
int registered; |
||||
}; |
||||
|
||||
/*
|
||||
* Given a pointer to the MTD object in the mtd_part structure, we can retrieve |
||||
* the pointer to that structure with this macro. |
||||
*/ |
||||
#define PART(x) ((struct mtd_part *)(x)) |
||||
|
||||
|
||||
/*
|
||||
* MTD methods which simply translate the effective address and pass through |
||||
* to the _real_ device. |
||||
*/ |
||||
|
||||
static int part_read (struct mtd_info *mtd, loff_t from, size_t len, |
||||
size_t *retlen, u_char *buf) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
int res; |
||||
|
||||
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++; |
||||
if (res == -EBADMSG) |
||||
mtd->ecc_stats.failed++; |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
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); |
||||
} |
||||
|
||||
static void 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); |
||||
} |
||||
#endif |
||||
|
||||
static int part_read_oob(struct mtd_info *mtd, loff_t from, |
||||
struct mtd_oob_ops *ops) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
int res; |
||||
|
||||
if (from >= mtd->size) |
||||
return -EINVAL; |
||||
if (ops->datbuf && from + ops->len > mtd->size) |
||||
return -EINVAL; |
||||
res = part->master->read_oob(part->master, from + part->offset, ops); |
||||
|
||||
if (unlikely(res)) { |
||||
if (res == -EUCLEAN) |
||||
mtd->ecc_stats.corrected++; |
||||
if (res == -EBADMSG) |
||||
mtd->ecc_stats.failed++; |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
static int part_get_user_prot_info (struct mtd_info *mtd, |
||||
struct otp_info *buf, size_t len) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
return part->master->get_user_prot_info (part->master, buf, len); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
static int part_get_fact_prot_info (struct mtd_info *mtd, |
||||
struct otp_info *buf, size_t len) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
return part->master->get_fact_prot_info (part->master, buf, len); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
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); |
||||
} |
||||
#endif |
||||
|
||||
static int part_write_oob(struct mtd_info *mtd, loff_t to, |
||||
struct mtd_oob_ops *ops) |
||||
{ |
||||
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); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
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); |
||||
} |
||||
#endif |
||||
|
||||
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); |
||||
if (ret) { |
||||
if (instr->fail_addr != 0xffffffff) |
||||
instr->fail_addr -= part->offset; |
||||
instr->addr -= part->offset; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
void mtd_erase_callback(struct erase_info *instr) |
||||
{ |
||||
if (instr->mtd->erase == part_erase) { |
||||
struct mtd_part *part = PART(instr->mtd); |
||||
|
||||
if (instr->fail_addr != 0xffffffff) |
||||
instr->fail_addr -= part->offset; |
||||
instr->addr -= part->offset; |
||||
} |
||||
if (instr->callback) |
||||
instr->callback(instr); |
||||
} |
||||
#ifdef MTD_LINUX |
||||
EXPORT_SYMBOL_GPL(mtd_erase_callback); |
||||
#endif |
||||
|
||||
#ifdef MTD_LINUX |
||||
static int part_lock (struct mtd_info *mtd, loff_t ofs, size_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); |
||||
} |
||||
|
||||
static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_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); |
||||
} |
||||
#endif |
||||
|
||||
static void part_sync(struct mtd_info *mtd) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
part->master->sync(part->master); |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
static int part_suspend(struct mtd_info *mtd) |
||||
{ |
||||
struct mtd_part *part = PART(mtd); |
||||
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); |
||||
} |
||||
#endif |
||||
|
||||
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); |
||||
} |
||||
|
||||
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); |
||||
#ifdef MTD_LINUX |
||||
if (!res) |
||||
mtd->ecc_stats.badblocks++; |
||||
#endif |
||||
return res; |
||||
} |
||||
|
||||
/*
|
||||
* This function unregisters and destroy all slave MTD objects which are |
||||
* attached to the given master MTD object. |
||||
*/ |
||||
|
||||
int del_mtd_partitions(struct mtd_info *master) |
||||
{ |
||||
struct list_head *node; |
||||
struct mtd_part *slave; |
||||
|
||||
for (node = mtd_partitions.next; |
||||
node != &mtd_partitions; |
||||
node = node->next) { |
||||
slave = list_entry(node, struct mtd_part, list); |
||||
if (slave->master == master) { |
||||
struct list_head *prev = node->prev; |
||||
__list_del(prev, node->next); |
||||
if(slave->registered) |
||||
del_mtd_device(&slave->mtd); |
||||
kfree(slave); |
||||
node = prev; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* This function, given a master MTD object and a partition table, creates |
||||
* and registers slave MTD objects which are bound to the master according to |
||||
* the partition definitions. |
||||
* (Q: should we register the master MTD object as well?) |
||||
*/ |
||||
|
||||
int add_mtd_partitions(struct mtd_info *master, |
||||
const struct mtd_partition *parts, |
||||
int nbparts) |
||||
{ |
||||
struct mtd_part *slave; |
||||
u_int32_t cur_offset = 0; |
||||
int i; |
||||
|
||||
printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); |
||||
|
||||
for (i = 0; i < nbparts; i++) { |
||||
|
||||
/* allocate the partition structure */ |
||||
slave = kzalloc (sizeof(*slave), GFP_KERNEL); |
||||
if (!slave) { |
||||
printk ("memory allocation error while creating partitions for \"%s\"\n", |
||||
master->name); |
||||
del_mtd_partitions(master); |
||||
return -ENOMEM; |
||||
} |
||||
list_add(&slave->list, &mtd_partitions); |
||||
|
||||
/* set up the MTD object for this partition */ |
||||
slave->mtd.type = master->type; |
||||
slave->mtd.flags = master->flags & ~parts[i].mask_flags; |
||||
slave->mtd.size = parts[i].size; |
||||
slave->mtd.writesize = master->writesize; |
||||
slave->mtd.oobsize = master->oobsize; |
||||
slave->mtd.oobavail = master->oobavail; |
||||
slave->mtd.subpage_sft = master->subpage_sft; |
||||
|
||||
slave->mtd.name = parts[i].name; |
||||
slave->mtd.owner = master->owner; |
||||
|
||||
slave->mtd.read = part_read; |
||||
slave->mtd.write = part_write; |
||||
|
||||
#ifdef MTD_LINUX |
||||
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; |
||||
} |
||||
#endif |
||||
|
||||
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; |
||||
#ifdef MTD_LINUX |
||||
if (!i && 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; |
||||
#endif |
||||
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 = parts[i].offset; |
||||
slave->index = i; |
||||
|
||||
if (slave->offset == MTDPART_OFS_APPEND) |
||||
slave->offset = cur_offset; |
||||
if (slave->offset == MTDPART_OFS_NXTBLK) { |
||||
slave->offset = cur_offset; |
||||
if ((cur_offset % master->erasesize) != 0) { |
||||
/* Round up to next erasesize */ |
||||
slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; |
||||
printk(KERN_NOTICE "Moving partition %d: " |
||||
"0x%08x -> 0x%08x\n", i, |
||||
cur_offset, slave->offset); |
||||
} |
||||
} |
||||
if (slave->mtd.size == MTDPART_SIZ_FULL) |
||||
slave->mtd.size = master->size - slave->offset; |
||||
cur_offset = slave->offset + slave->mtd.size; |
||||
|
||||
printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, |
||||
slave->offset + slave->mtd.size, slave->mtd.name); |
||||
|
||||
/* let's do some sanity checks */ |
||||
if (slave->offset >= master->size) { |
||||
/* let's register it anyway to preserve ordering */ |
||||
slave->offset = 0; |
||||
slave->mtd.size = 0; |
||||
printk ("mtd: partition \"%s\" is out of reach -- disabled\n", |
||||
parts[i].name); |
||||
} |
||||
if (slave->offset + slave->mtd.size > master->size) { |
||||
slave->mtd.size = master->size - slave->offset; |
||||
printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", |
||||
parts[i].name, master->name, slave->mtd.size); |
||||
} |
||||
if (master->numeraseregions>1) { |
||||
/* Deal with variable erase size stuff */ |
||||
int i; |
||||
struct mtd_erase_region_info *regions = master->eraseregions; |
||||
|
||||
/* Find the first erase regions which is part of this partition. */ |
||||
for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) |
||||
; |
||||
|
||||
for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { |
||||
if (slave->mtd.erasesize < regions[i].erasesize) { |
||||
slave->mtd.erasesize = regions[i].erasesize; |
||||
} |
||||
} |
||||
} else { |
||||
/* Single erase size */ |
||||
slave->mtd.erasesize = master->erasesize; |
||||
} |
||||
|
||||
if ((slave->mtd.flags & MTD_WRITEABLE) && |
||||
(slave->offset % slave->mtd.erasesize)) { |
||||
/* Doesn't start on a boundary of major erase size */ |
||||
/* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ |
||||
slave->mtd.flags &= ~MTD_WRITEABLE; |
||||
printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", |
||||
parts[i].name); |
||||
} |
||||
if ((slave->mtd.flags & MTD_WRITEABLE) && |
||||
(slave->mtd.size % slave->mtd.erasesize)) { |
||||
slave->mtd.flags &= ~MTD_WRITEABLE; |
||||
printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", |
||||
parts[i].name); |
||||
} |
||||
|
||||
slave->mtd.ecclayout = master->ecclayout; |
||||
if (master->block_isbad) { |
||||
uint32_t offs = 0; |
||||
|
||||
while(offs < slave->mtd.size) { |
||||
if (master->block_isbad(master, |
||||
offs + slave->offset)) |
||||
slave->mtd.ecc_stats.badblocks++; |
||||
offs += slave->mtd.erasesize; |
||||
} |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
if (parts[i].mtdp) { |
||||
/* store the object pointer
|
||||
* (caller may or may not register it */ |
||||
*parts[i].mtdp = &slave->mtd; |
||||
slave->registered = 0; |
||||
} else { |
||||
/* register our partition */ |
||||
add_mtd_device(&slave->mtd); |
||||
slave->registered = 1; |
||||
} |
||||
#else |
||||
/* register our partition */ |
||||
add_mtd_device(&slave->mtd); |
||||
slave->registered = 1; |
||||
#endif |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
#ifdef MTD_LINUX |
||||
EXPORT_SYMBOL(add_mtd_partitions); |
||||
EXPORT_SYMBOL(del_mtd_partitions); |
||||
#endif |
Loading…
Reference in new issue