2005-04-17 02:20:36 +04:00
/*
* Simple MTD partitioning layer
*
2010-08-08 23:58:20 +04:00
* Copyright © 2000 Nicolas Pitre < nico @ fluxnic . net >
* Copyright © 2002 Thomas Gleixner < gleixner @ linutronix . de >
* Copyright © 2000 - 2010 David Woodhouse < dwmw2 @ infradead . org >
2005-04-17 02:20:36 +04:00
*
2010-08-08 23:58:20 +04:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*
2005-11-07 14:15:26 +03:00
*/
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/kmod.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2010-09-17 14:31:41 +04:00
# include <linux/err.h>
2015-04-03 01:15:10 +03:00
# include <linux/kconfig.h>
2005-04-17 02:20:36 +04:00
2011-05-23 13:23:42 +04:00
# include "mtdcore.h"
2005-04-17 02:20:36 +04:00
/* Our partition linked list */
static LIST_HEAD ( mtd_partitions ) ;
2010-09-17 14:31:41 +04:00
static DEFINE_MUTEX ( mtd_partitions_mutex ) ;
2005-04-17 02:20:36 +04:00
/* Our partition node structure */
struct mtd_part {
struct mtd_info mtd ;
struct mtd_info * master ;
2008-12-10 16:37:21 +03:00
uint64_t offset ;
2005-04-17 02:20:36 +04:00
struct list_head list ;
} ;
/*
* Given a pointer to the MTD object in the mtd_part structure , we can retrieve
2015-11-20 06:28:39 +03:00
* the pointer to that structure .
2005-04-17 02:20:36 +04:00
*/
2015-11-20 06:28:39 +03:00
static inline struct mtd_part * mtd_to_part ( const struct mtd_info * mtd )
{
return container_of ( mtd , struct mtd_part , mtd ) ;
}
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:26 +03:00
/*
2005-04-17 02:20:36 +04:00
* MTD methods which simply translate the effective address and pass through
* to the _real_ device .
*/
2008-07-18 20:00:33 +04:00
static int part_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2009-03-27 01:41:09 +03:00
struct mtd_ecc_stats stats ;
2006-05-30 02:37:34 +04:00
int res ;
2009-03-27 01:41:09 +03:00
stats = part - > master - > ecc_stats ;
2012-03-04 01:13:06 +04:00
res = part - > master - > _read ( part - > master , from + part - > offset , len ,
retlen , buf ) ;
2012-04-25 23:06:11 +04:00
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 ;
2006-05-30 02:37:34 +04:00
return res ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_point ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , void * * virt , resource_size_t * phys )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-02-03 18:23:52 +04:00
2012-03-04 01:13:06 +04:00
return part - > master - > _point ( part - > master , from + part - > offset , len ,
retlen , virt , phys ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 19:21:03 +04:00
2012-02-03 15:20:43 +04:00
static int part_unpoint ( struct mtd_info * mtd , loff_t from , size_t len )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2005-04-17 02:20:36 +04:00
2012-03-04 01:13:06 +04:00
return part - > master - > _unpoint ( part - > master , from + part - > offset , len ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-12 13:40:00 +03:00
static unsigned long part_get_unmapped_area ( struct mtd_info * mtd ,
unsigned long len ,
unsigned long offset ,
unsigned long flags )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2009-02-12 13:40:00 +03:00
offset + = part - > offset ;
2012-03-04 01:13:06 +04:00
return part - > master - > _get_unmapped_area ( part - > master , len , offset ,
flags ) ;
2009-02-12 13:40:00 +03:00
}
2006-05-29 05:26:58 +04:00
static int part_read_oob ( struct mtd_info * mtd , loff_t from ,
2008-07-18 20:00:33 +04:00
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2006-05-30 02:37:34 +04:00
int res ;
2006-05-29 05:26:58 +04:00
2005-04-17 02:20:36 +04:00
if ( from > = mtd - > size )
2006-05-29 05:26:58 +04:00
return - EINVAL ;
2006-11-03 18:20:38 +03:00
if ( ops - > datbuf & & from + ops - > len > mtd - > size )
2006-05-29 05:26:58 +04:00
return - EINVAL ;
2006-05-30 02:37:34 +04:00
2011-01-16 18:50:54 +03:00
/*
* 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 ;
2011-08-31 05:45:40 +04:00
if ( ops - > mode = = MTD_OPS_AUTO_OOB )
2011-01-16 18:50:54 +03:00
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 ;
}
2012-03-04 01:13:06 +04:00
res = part - > master - > _read_oob ( part - > master , from + part - > offset , ops ) ;
2006-05-30 02:37:34 +04:00
if ( unlikely ( res ) ) {
2011-09-21 05:34:25 +04:00
if ( mtd_is_bitflip ( res ) )
2006-05-30 02:37:34 +04:00
mtd - > ecc_stats . corrected + + ;
2011-09-21 05:34:25 +04:00
if ( mtd_is_eccerr ( res ) )
2006-05-30 02:37:34 +04:00
mtd - > ecc_stats . failed + + ;
}
return res ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_read_user_prot_reg ( struct mtd_info * mtd , loff_t from ,
size_t len , size_t * retlen , u_char * buf )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _read_user_prot_reg ( part - > master , from , len ,
retlen , buf ) ;
2005-04-17 02:20:36 +04:00
}
2014-01-28 12:29:44 +04:00
static int part_get_user_prot_info ( struct mtd_info * mtd , size_t len ,
size_t * retlen , struct otp_info * buf )
2005-02-08 20:11:19 +03:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2014-01-28 12:29:44 +04:00
return part - > master - > _get_user_prot_info ( part - > master , len , retlen ,
buf ) ;
2005-02-08 20:11:19 +03:00
}
2008-07-18 20:00:33 +04:00
static int part_read_fact_prot_reg ( struct mtd_info * mtd , loff_t from ,
size_t len , size_t * retlen , u_char * buf )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _read_fact_prot_reg ( part - > master , from , len ,
retlen , buf ) ;
2005-04-17 02:20:36 +04:00
}
2014-01-28 12:29:44 +04:00
static int part_get_fact_prot_info ( struct mtd_info * mtd , size_t len ,
size_t * retlen , struct otp_info * buf )
2005-02-08 20:11:19 +03:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2014-01-28 12:29:44 +04:00
return part - > master - > _get_fact_prot_info ( part - > master , len , retlen ,
buf ) ;
2005-02-08 20:11:19 +03:00
}
2008-07-18 20:00:33 +04:00
static int part_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _write ( part - > master , to + part - > offset , len ,
retlen , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_panic_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
2008-02-06 13:17:15 +03:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _panic_write ( part - > master , to + part - > offset , len ,
retlen , buf ) ;
2008-02-06 13:17:15 +03:00
}
2006-05-29 05:26:58 +04:00
static int part_write_oob ( struct mtd_info * mtd , loff_t to ,
2008-07-18 20:00:33 +04:00
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2006-05-29 05:26:58 +04:00
2005-04-17 02:20:36 +04:00
if ( to > = mtd - > size )
2006-05-29 05:26:58 +04:00
return - EINVAL ;
2006-11-03 18:20:38 +03:00
if ( ops - > datbuf & & to + ops - > len > mtd - > size )
2006-05-29 05:26:58 +04:00
return - EINVAL ;
2012-03-04 01:13:06 +04:00
return part - > master - > _write_oob ( part - > master , to + part - > offset , ops ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_write_user_prot_reg ( struct mtd_info * mtd , loff_t from ,
size_t len , size_t * retlen , u_char * buf )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _write_user_prot_reg ( part - > master , from , len ,
retlen , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_lock_user_prot_reg ( struct mtd_info * mtd , loff_t from ,
size_t len )
2005-02-08 20:11:19 +03:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _lock_user_prot_reg ( part - > master , from , len ) ;
2005-02-08 20:11:19 +03:00
}
2008-07-18 20:00:33 +04:00
static int part_writev ( struct mtd_info * mtd , const struct kvec * vecs ,
unsigned long count , loff_t to , size_t * retlen )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _writev ( part - > master , vecs , count ,
to + part - > offset , retlen ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_erase ( struct mtd_info * mtd , struct erase_info * instr )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2005-04-17 02:20:36 +04:00
int ret ;
2012-02-03 20:13:23 +04:00
2005-04-17 02:20:36 +04:00
instr - > addr + = part - > offset ;
2012-03-04 01:13:06 +04:00
ret = part - > master - > _erase ( part - > master , instr ) ;
2007-03-08 13:20:12 +03:00
if ( ret ) {
2008-08-12 13:40:50 +04:00
if ( instr - > fail_addr ! = MTD_FAIL_ADDR_UNKNOWN )
2007-03-08 13:20:12 +03:00
instr - > fail_addr - = part - > offset ;
instr - > addr - = part - > offset ;
}
2005-04-17 02:20:36 +04:00
return ret ;
}
void mtd_erase_callback ( struct erase_info * instr )
{
2012-01-30 16:58:32 +04:00
if ( instr - > mtd - > _erase = = part_erase ) {
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( instr - > mtd ) ;
2005-04-17 02:20:36 +04:00
2008-08-12 13:40:50 +04:00
if ( instr - > fail_addr ! = MTD_FAIL_ADDR_UNKNOWN )
2005-04-17 02:20:36 +04:00
instr - > fail_addr - = part - > offset ;
instr - > addr - = part - > offset ;
}
if ( instr - > callback )
instr - > callback ( instr ) ;
}
EXPORT_SYMBOL_GPL ( mtd_erase_callback ) ;
2008-12-10 16:37:21 +03:00
static int part_lock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _lock ( part - > master , ofs + part - > offset , len ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-10 16:37:21 +03:00
static int part_unlock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _unlock ( part - > master , ofs + part - > offset , len ) ;
2005-04-17 02:20:36 +04:00
}
2010-06-14 20:10:33 +04:00
static int part_is_locked ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _is_locked ( part - > master , ofs + part - > offset , len ) ;
2010-06-14 20:10:33 +04:00
}
2005-04-17 02:20:36 +04:00
static void part_sync ( struct mtd_info * mtd )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
part - > master - > _sync ( part - > master ) ;
2005-04-17 02:20:36 +04:00
}
static int part_suspend ( struct mtd_info * mtd )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
return part - > master - > _suspend ( part - > master ) ;
2005-04-17 02:20:36 +04:00
}
static void part_resume ( struct mtd_info * mtd )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2012-03-04 01:13:06 +04:00
part - > master - > _resume ( part - > master ) ;
2005-04-17 02:20:36 +04:00
}
2014-05-22 02:06:12 +04:00
static int part_block_isreserved ( struct mtd_info * mtd , loff_t ofs )
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2014-05-22 02:06:12 +04:00
ofs + = part - > offset ;
return part - > master - > _block_isreserved ( part - > master , ofs ) ;
}
2008-07-18 20:00:33 +04:00
static int part_block_isbad ( struct mtd_info * mtd , loff_t ofs )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2005-04-17 02:20:36 +04:00
ofs + = part - > offset ;
2012-03-04 01:13:06 +04:00
return part - > master - > _block_isbad ( part - > master , ofs ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 20:00:33 +04:00
static int part_block_markbad ( struct mtd_info * mtd , loff_t ofs )
2005-04-17 02:20:36 +04:00
{
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2006-05-30 02:37:34 +04:00
int res ;
2005-04-17 02:20:36 +04:00
ofs + = part - > offset ;
2012-03-04 01:13:06 +04:00
res = part - > master - > _block_markbad ( part - > master , ofs ) ;
2006-05-30 02:37:34 +04:00
if ( ! res )
mtd - > ecc_stats . badblocks + + ;
return res ;
2005-04-17 02:20:36 +04:00
}
2010-09-17 14:31:41 +04:00
static inline void free_partition ( struct mtd_part * p )
{
kfree ( p - > mtd . name ) ;
kfree ( p ) ;
}
2005-11-07 14:15:26 +03:00
/*
* This function unregisters and destroy all slave MTD objects which are
2005-04-17 02:20:36 +04:00
* attached to the given master MTD object .
*/
int del_mtd_partitions ( struct mtd_info * master )
{
2008-05-19 23:11:50 +04:00
struct mtd_part * slave , * next ;
2010-09-17 14:31:41 +04:00
int ret , err = 0 ;
2005-04-17 02:20:36 +04:00
2010-09-17 14:31:41 +04:00
mutex_lock ( & mtd_partitions_mutex ) ;
2008-05-19 23:11:50 +04:00
list_for_each_entry_safe ( slave , next , & mtd_partitions , list )
2005-04-17 02:20:36 +04:00
if ( slave - > master = = master ) {
2010-09-17 14:31:41 +04:00
ret = del_mtd_device ( & slave - > mtd ) ;
if ( ret < 0 ) {
err = ret ;
continue ;
}
2008-05-19 23:11:50 +04:00
list_del ( & slave - > list ) ;
2010-09-17 14:31:41 +04:00
free_partition ( slave ) ;
2005-04-17 02:20:36 +04:00
}
2010-09-17 14:31:41 +04:00
mutex_unlock ( & mtd_partitions_mutex ) ;
2005-04-17 02:20:36 +04:00
2010-09-17 14:31:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2010-09-17 14:31:41 +04:00
static struct mtd_part * allocate_partition ( struct mtd_info * master ,
const struct mtd_partition * part , int partno ,
uint64_t cur_offset )
2008-07-18 20:00:18 +04:00
{
struct mtd_part * slave ;
2010-09-17 14:31:41 +04:00
char * name ;
2008-07-18 20:00:18 +04:00
/* allocate the partition structure */
2008-07-18 20:00:33 +04:00
slave = kzalloc ( sizeof ( * slave ) , GFP_KERNEL ) ;
2010-09-17 14:31:41 +04:00
name = kstrdup ( part - > name , GFP_KERNEL ) ;
if ( ! name | | ! slave ) {
2008-07-18 20:00:33 +04:00
printk ( KERN_ERR " memory allocation error while creating partitions for \" %s \" \n " ,
2010-09-17 14:31:41 +04:00
master - > name ) ;
kfree ( name ) ;
kfree ( slave ) ;
return ERR_PTR ( - ENOMEM ) ;
2008-07-18 20:00:18 +04:00
}
/* set up the MTD object for this partition */
slave - > mtd . type = master - > type ;
slave - > mtd . flags = master - > flags & ~ part - > mask_flags ;
slave - > mtd . size = part - > size ;
slave - > mtd . writesize = master - > writesize ;
2010-12-17 01:42:18 +03:00
slave - > mtd . writebufsize = master - > writebufsize ;
2008-07-18 20:00:18 +04:00
slave - > mtd . oobsize = master - > oobsize ;
slave - > mtd . oobavail = master - > oobavail ;
slave - > mtd . subpage_sft = master - > subpage_sft ;
2010-09-17 14:31:41 +04:00
slave - > mtd . name = name ;
2008-07-18 20:00:18 +04:00
slave - > mtd . owner = master - > owner ;
2015-04-03 01:15:10 +03:00
/* NOTE: Historically, we didn't arrange MTDs as a tree out of
* concern for showing the same data in multiple partitions .
* However , it is very useful to have the master node present ,
* so the MTD_PARTITIONED_MASTER option allows that . The master
* will have device nodes etc only if this is set , so make the
* parent conditional on that option . Note , this is a way to
* distinguish between the master and the partition in sysfs .
2009-03-26 10:42:41 +03:00
*/
2015-04-03 01:15:10 +03:00
slave - > mtd . dev . parent = IS_ENABLED ( CONFIG_MTD_PARTITIONED_MASTER ) ?
& master - > dev :
master - > dev . parent ;
2009-03-26 10:42:41 +03:00
2012-01-30 16:58:32 +04:00
slave - > mtd . _read = part_read ;
slave - > mtd . _write = part_write ;
2008-07-18 20:00:18 +04:00
2012-01-30 16:58:32 +04:00
if ( master - > _panic_write )
slave - > mtd . _panic_write = part_panic_write ;
2008-07-18 20:00:18 +04:00
2012-01-30 16:58:32 +04:00
if ( master - > _point & & master - > _unpoint ) {
slave - > mtd . _point = part_point ;
slave - > mtd . _unpoint = part_unpoint ;
2008-07-18 20:00:18 +04:00
}
2012-01-30 16:58:32 +04:00
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 ;
2008-07-18 20:00:18 +04:00
}
2012-01-30 16:58:32 +04:00
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 ;
2014-05-22 02:06:12 +04:00
if ( master - > _block_isreserved )
slave - > mtd . _block_isreserved = part_block_isreserved ;
2012-01-30 16:58:32 +04:00
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 ;
2008-07-18 20:00:18 +04:00
slave - > master = master ;
slave - > offset = part - > offset ;
if ( slave - > offset = = MTDPART_OFS_APPEND )
slave - > offset = cur_offset ;
if ( slave - > offset = = MTDPART_OFS_NXTBLK ) {
slave - > offset = cur_offset ;
2008-12-10 16:37:21 +03:00
if ( mtd_mod_by_eb ( cur_offset , master ) ! = 0 ) {
2008-07-18 20:00:18 +04:00
/* Round up to next erasesize */
2008-12-10 16:37:21 +03:00
slave - > offset = ( mtd_div_by_eb ( cur_offset , master ) + 1 ) * master - > erasesize ;
2008-07-18 20:00:18 +04:00
printk ( KERN_NOTICE " Moving partition %d: "
2008-12-10 16:37:21 +03:00
" 0x%012llx -> 0x%012llx \n " , partno ,
( unsigned long long ) cur_offset , ( unsigned long long ) slave - > offset ) ;
2008-07-18 20:00:18 +04:00
}
}
2011-06-06 18:04:14 +04:00
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 ;
}
}
2008-07-18 20:00:18 +04:00
if ( slave - > mtd . size = = MTDPART_SIZ_FULL )
slave - > mtd . size = master - > size - slave - > offset ;
2008-12-10 16:37:21 +03:00
printk ( KERN_NOTICE " 0x%012llx-0x%012llx : \" %s \" \n " , ( unsigned long long ) slave - > offset ,
( unsigned long long ) ( slave - > offset + slave - > mtd . size ) , slave - > mtd . name ) ;
2008-07-18 20:00:18 +04:00
/* let's do some sanity checks */
if ( slave - > offset > = master - > size ) {
2008-07-18 20:01:22 +04:00
/* let's register it anyway to preserve ordering */
2008-07-18 20:00:18 +04:00
slave - > offset = 0 ;
slave - > mtd . size = 0 ;
2008-07-18 20:00:33 +04:00
printk ( KERN_ERR " mtd: partition \" %s \" is out of reach -- disabled \n " ,
2008-07-18 20:00:18 +04:00
part - > name ) ;
2008-07-18 20:01:22 +04:00
goto out_register ;
2008-07-18 20:00:18 +04:00
}
if ( slave - > offset + slave - > mtd . size > master - > size ) {
slave - > mtd . size = master - > size - slave - > offset ;
2008-12-10 16:37:21 +03:00
printk ( KERN_WARNING " mtd: partition \" %s \" extends beyond the end of device \" %s \" -- size truncated to %#llx \n " ,
part - > name , master - > name , ( unsigned long long ) slave - > mtd . size ) ;
2008-07-18 20:00:18 +04:00
}
2008-07-18 20:00:33 +04:00
if ( master - > numeraseregions > 1 ) {
2008-07-18 20:00:18 +04:00
/* Deal with variable erase size stuff */
2008-07-18 20:00:57 +04:00
int i , max = master - > numeraseregions ;
2008-12-10 16:37:21 +03:00
u64 end = slave - > offset + slave - > mtd . size ;
2008-07-18 20:00:18 +04:00
struct mtd_erase_region_info * regions = master - > eraseregions ;
2008-07-18 20:00:57 +04:00
/* Find the first erase regions which is part of this
* partition . */
for ( i = 0 ; i < max & & regions [ i ] . offset < = slave - > offset ; i + + )
2008-07-18 20:00:18 +04:00
;
2008-07-18 20:00:57 +04:00
/* The loop searched for the region _behind_ the first one */
2009-09-18 23:51:50 +04:00
if ( i > 0 )
i - - ;
2008-07-18 20:00:18 +04:00
2008-07-18 20:00:57 +04:00
/* Pick biggest erasesize */
for ( ; i < max & & regions [ i ] . offset < end ; i + + ) {
2008-07-18 20:00:18 +04:00
if ( slave - > mtd . erasesize < regions [ i ] . erasesize ) {
slave - > mtd . erasesize = regions [ i ] . erasesize ;
}
}
2008-07-18 20:00:57 +04:00
BUG_ON ( slave - > mtd . erasesize = = 0 ) ;
2008-07-18 20:00:18 +04:00
} else {
/* Single erase size */
slave - > mtd . erasesize = master - > erasesize ;
}
if ( ( slave - > mtd . flags & MTD_WRITEABLE ) & &
2008-12-10 16:37:21 +03:00
mtd_mod_by_eb ( slave - > offset , & slave - > mtd ) ) {
2008-07-18 20:00:18 +04:00
/* Doesn't start on a boundary of major erase size */
2008-07-18 20:00:33 +04:00
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
2008-07-18 20:00:18 +04:00
slave - > mtd . flags & = ~ MTD_WRITEABLE ;
2008-07-18 20:00:33 +04:00
printk ( KERN_WARNING " mtd: partition \" %s \" doesn't start on an erase block boundary -- force read-only \n " ,
2008-07-18 20:00:18 +04:00
part - > name ) ;
}
if ( ( slave - > mtd . flags & MTD_WRITEABLE ) & &
2008-12-10 16:37:21 +03:00
mtd_mod_by_eb ( slave - > mtd . size , & slave - > mtd ) ) {
2008-07-18 20:00:18 +04:00
slave - > mtd . flags & = ~ MTD_WRITEABLE ;
2008-07-18 20:00:33 +04:00
printk ( KERN_WARNING " mtd: partition \" %s \" doesn't end on an erase block -- force read-only \n " ,
2008-07-18 20:00:18 +04:00
part - > name ) ;
}
slave - > mtd . ecclayout = master - > ecclayout ;
2013-08-16 06:10:06 +04:00
slave - > mtd . ecc_step_size = master - > ecc_step_size ;
2012-03-12 01:21:11 +04:00
slave - > mtd . ecc_strength = master - > ecc_strength ;
2012-04-25 23:06:08 +04:00
slave - > mtd . bitflip_threshold = master - > bitflip_threshold ;
2012-01-30 16:58:32 +04:00
if ( master - > _block_isbad ) {
2008-12-10 16:37:21 +03:00
uint64_t offs = 0 ;
2008-07-18 20:00:18 +04:00
2008-07-18 20:00:33 +04:00
while ( offs < slave - > mtd . size ) {
2014-03-21 15:57:44 +04:00
if ( mtd_block_isreserved ( master , offs + slave - > offset ) )
slave - > mtd . ecc_stats . bbtblocks + + ;
else if ( mtd_block_isbad ( master , offs + slave - > offset ) )
2008-07-18 20:00:18 +04:00
slave - > mtd . ecc_stats . badblocks + + ;
offs + = slave - > mtd . erasesize ;
}
}
2008-07-18 20:01:22 +04:00
out_register :
2008-07-18 20:00:18 +04:00
return slave ;
}
2015-04-03 01:15:11 +03:00
static ssize_t mtd_partition_offset_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_get_drvdata ( dev ) ;
2015-11-20 06:28:39 +03:00
struct mtd_part * part = mtd_to_part ( mtd ) ;
2015-04-03 01:15:11 +03:00
return snprintf ( buf , PAGE_SIZE , " %lld \n " , part - > offset ) ;
}
static DEVICE_ATTR ( offset , S_IRUGO , mtd_partition_offset_show , NULL ) ;
static const struct attribute * mtd_partition_attrs [ ] = {
& dev_attr_offset . attr ,
NULL
} ;
static int mtd_add_partition_attrs ( struct mtd_part * new )
{
int ret = sysfs_create_files ( & new - > mtd . dev . kobj , mtd_partition_attrs ) ;
if ( ret )
printk ( KERN_WARNING
" mtd: failed to create partition attrs, err=%d \n " , ret ) ;
return ret ;
}
2013-11-12 23:11:26 +04:00
int mtd_add_partition ( struct mtd_info * master , const char * name ,
2010-09-17 14:31:41 +04:00
long long offset , long long length )
{
struct mtd_partition part ;
2015-04-03 01:15:12 +03:00
struct mtd_part * new ;
2010-09-17 14:31:41 +04:00
int ret = 0 ;
/* the direct offset is expected */
if ( offset = = MTDPART_OFS_APPEND | |
offset = = MTDPART_OFS_NXTBLK )
return - EINVAL ;
if ( length = = MTDPART_SIZ_FULL )
length = master - > size - offset ;
if ( length < = 0 )
return - EINVAL ;
2015-11-12 03:47:52 +03:00
memset ( & part , 0 , sizeof ( part ) ) ;
2010-09-17 14:31:41 +04:00
part . name = name ;
part . size = length ;
part . offset = offset ;
new = allocate_partition ( master , & part , - 1 , offset ) ;
if ( IS_ERR ( new ) )
return PTR_ERR ( new ) ;
mutex_lock ( & mtd_partitions_mutex ) ;
list_add ( & new - > list , & mtd_partitions ) ;
mutex_unlock ( & mtd_partitions_mutex ) ;
add_mtd_device ( & new - > mtd ) ;
2015-04-03 01:15:11 +03:00
mtd_add_partition_attrs ( new ) ;
2010-09-17 14:31:41 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( mtd_add_partition ) ;
int mtd_del_partition ( struct mtd_info * master , int partno )
{
struct mtd_part * slave , * next ;
int ret = - EINVAL ;
mutex_lock ( & mtd_partitions_mutex ) ;
list_for_each_entry_safe ( slave , next , & mtd_partitions , list )
if ( ( slave - > master = = master ) & &
( slave - > mtd . index = = partno ) ) {
2015-04-03 01:15:11 +03:00
sysfs_remove_files ( & slave - > mtd . dev . kobj ,
mtd_partition_attrs ) ;
2010-09-17 14:31:41 +04:00
ret = del_mtd_device ( & slave - > mtd ) ;
if ( ret < 0 )
break ;
list_del ( & slave - > list ) ;
free_partition ( slave ) ;
break ;
}
mutex_unlock ( & mtd_partitions_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( mtd_del_partition ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 .
2009-03-26 10:42:41 +03:00
*
2015-04-03 01:15:10 +03:00
* For historical reasons , this function ' s caller only registers the master
* if the MTD_PARTITIONED_MASTER config option is set .
2005-04-17 02:20:36 +04:00
*/
2005-11-07 14:15:26 +03:00
int add_mtd_partitions ( struct mtd_info * master ,
2005-04-17 02:20:36 +04:00
const struct mtd_partition * parts ,
int nbparts )
{
struct mtd_part * slave ;
2008-12-10 16:37:21 +03:00
uint64_t cur_offset = 0 ;
2005-04-17 02:20:36 +04:00
int i ;
2008-07-18 20:00:33 +04:00
printk ( KERN_NOTICE " Creating %d MTD partitions on \" %s \" : \n " , nbparts , master - > name ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < nbparts ; i + + ) {
2010-09-17 14:31:41 +04:00
slave = allocate_partition ( master , parts + i , i , cur_offset ) ;
2015-07-30 13:18:03 +03:00
if ( IS_ERR ( slave ) ) {
del_mtd_partitions ( master ) ;
2010-09-17 14:31:41 +04:00
return PTR_ERR ( slave ) ;
2015-07-30 13:18:03 +03:00
}
2010-09-17 14:31:41 +04:00
mutex_lock ( & mtd_partitions_mutex ) ;
list_add ( & slave - > list , & mtd_partitions ) ;
mutex_unlock ( & mtd_partitions_mutex ) ;
add_mtd_device ( & slave - > mtd ) ;
2015-04-03 01:15:11 +03:00
mtd_add_partition_attrs ( slave ) ;
2010-09-17 14:31:41 +04:00
2005-04-17 02:20:36 +04:00
cur_offset = slave - > offset + slave - > mtd . size ;
}
return 0 ;
}
static DEFINE_SPINLOCK ( part_parser_lock ) ;
static LIST_HEAD ( part_parsers ) ;
2015-12-05 02:25:15 +03:00
static struct mtd_part_parser * mtd_part_parser_get ( const char * name )
2005-04-17 02:20:36 +04:00
{
2008-05-19 23:11:50 +04:00
struct mtd_part_parser * p , * ret = NULL ;
2005-04-17 02:20:36 +04:00
2008-05-19 23:11:50 +04:00
spin_lock ( & part_parser_lock ) ;
2005-04-17 02:20:36 +04:00
2008-05-19 23:11:50 +04:00
list_for_each_entry ( p , & part_parsers , list )
2005-04-17 02:20:36 +04:00
if ( ! strcmp ( p - > name , name ) & & try_module_get ( p - > owner ) ) {
ret = p ;
break ;
}
2008-05-19 23:11:50 +04:00
2005-04-17 02:20:36 +04:00
spin_unlock ( & part_parser_lock ) ;
return ret ;
}
2015-12-05 02:25:15 +03:00
static inline void mtd_part_parser_put ( const struct mtd_part_parser * p )
{
module_put ( p - > owner ) ;
}
2011-06-23 15:26:14 +04:00
2015-11-12 06:13:29 +03:00
int __register_mtd_parser ( struct mtd_part_parser * p , struct module * owner )
2005-04-17 02:20:36 +04:00
{
2015-11-12 06:13:29 +03:00
p - > owner = owner ;
2005-04-17 02:20:36 +04:00
spin_lock ( & part_parser_lock ) ;
list_add ( & p - > list , & part_parsers ) ;
spin_unlock ( & part_parser_lock ) ;
2015-11-12 06:13:29 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2015-11-12 06:13:29 +03:00
EXPORT_SYMBOL_GPL ( __register_mtd_parser ) ;
2005-04-17 02:20:36 +04:00
2013-12-01 14:59:15 +04:00
void deregister_mtd_parser ( struct mtd_part_parser * p )
2005-04-17 02:20:36 +04:00
{
spin_lock ( & part_parser_lock ) ;
list_del ( & p - > list ) ;
spin_unlock ( & part_parser_lock ) ;
}
2008-07-18 20:00:33 +04:00
EXPORT_SYMBOL_GPL ( deregister_mtd_parser ) ;
2005-04-17 02:20:36 +04:00
2011-06-08 12:42:27 +04:00
/*
* Do not forget to update ' parse_mtd_partitions ( ) ' kerneldoc comment if you
* are changing this array !
*/
2013-03-12 12:51:35 +04:00
static const char * const default_mtd_part_types [ ] = {
2011-05-29 21:32:33 +04:00
" cmdlinepart " ,
" ofpart " ,
NULL
} ;
2011-06-02 18:51:16 +04:00
2011-06-08 12:42:27 +04:00
/**
* parse_mtd_partitions - parse MTD partitions
* @ master : the master partition ( describes whole MTD device )
* @ types : names of partition parsers to try or % NULL
2015-12-05 02:25:17 +03:00
* @ pparts : info about partitions found is returned here
2011-06-10 18:18:28 +04:00
* @ data : MTD partition parser - specific data
2011-06-08 12:42:27 +04:00
*
* 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
2011-05-29 21:32:33 +04:00
* " cmdlinepart " and " ofpart " parsers ATM .
2012-08-18 21:07:41 +04:00
* Note : If there are more then one parser in @ types , the kernel only takes the
* partitions parsed out by the first parser .
2011-06-08 12:42:27 +04:00
*
* This function may return :
* o a negative error code in case of failure
2015-12-05 02:25:17 +03:00
* o zero otherwise , and @ pparts will describe the partitions , number of
* partitions , and the parser which parsed them
2011-06-08 12:42:27 +04:00
*/
2013-03-11 17:38:48 +04:00
int parse_mtd_partitions ( struct mtd_info * master , const char * const * types ,
2015-12-05 02:25:17 +03:00
struct mtd_partitions * pparts ,
2011-06-10 18:18:28 +04:00
struct mtd_part_parser_data * data )
2005-04-17 02:20:36 +04:00
{
struct mtd_part_parser * parser ;
2015-10-11 23:03:47 +03:00
int ret , err = 0 ;
2005-11-07 14:15:26 +03:00
2011-06-02 18:51:16 +04:00
if ( ! types )
types = default_mtd_part_types ;
2015-10-11 23:03:47 +03:00
for ( ; * types ; types + + ) {
2015-08-18 18:34:07 +03:00
pr_debug ( " %s: parsing partitions %s \n " , master - > name , * types ) ;
2015-12-05 02:25:15 +03:00
parser = mtd_part_parser_get ( * types ) ;
2005-04-17 02:20:36 +04:00
if ( ! parser & & ! request_module ( " %s " , * types ) )
2015-12-05 02:25:15 +03:00
parser = mtd_part_parser_get ( * types ) ;
2015-08-18 18:34:07 +03:00
pr_debug ( " %s: got parser %s \n " , master - > name ,
parser ? parser - > name : NULL ) ;
2011-05-17 12:13:17 +04:00
if ( ! parser )
2005-04-17 02:20:36 +04:00
continue ;
2015-12-05 02:25:17 +03:00
ret = ( * parser - > parse_fn ) ( master , & pparts - > parts , data ) ;
2015-08-18 18:34:07 +03:00
pr_debug ( " %s: parser %s: %i \n " ,
master - > name , parser - > name , ret ) ;
2015-12-05 02:25:15 +03:00
mtd_part_parser_put ( parser ) ;
2005-04-17 02:20:36 +04:00
if ( ret > 0 ) {
2005-11-07 14:15:26 +03:00
printk ( KERN_NOTICE " %d %s partitions found on MTD device %s \n " ,
2005-04-17 02:20:36 +04:00
ret , parser - > name , master - > name ) ;
2015-12-05 02:25:17 +03:00
pparts - > nr_parts = ret ;
pparts - > parser = parser ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2015-10-11 23:03:47 +03:00
/*
* Stash the first error we see ; only report it if no parser
* succeeds
*/
if ( ret < 0 & & ! err )
err = ret ;
2005-04-17 02:20:36 +04:00
}
2015-10-11 23:03:47 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2010-09-17 14:31:41 +04:00
2012-07-10 20:23:39 +04:00
int mtd_is_partition ( const struct mtd_info * mtd )
2010-09-17 14:31:41 +04:00
{
struct mtd_part * part ;
2010-11-23 15:17:17 +03:00
int ispart = 0 ;
2010-09-17 14:31:41 +04:00
mutex_lock ( & mtd_partitions_mutex ) ;
list_for_each_entry ( part , & mtd_partitions , list )
if ( & part - > mtd = = mtd ) {
2010-11-23 15:17:17 +03:00
ispart = 1 ;
2010-09-17 14:31:41 +04:00
break ;
}
mutex_unlock ( & mtd_partitions_mutex ) ;
2010-11-23 15:17:17 +03:00
return ispart ;
2010-09-17 14:31:41 +04:00
}
2010-11-23 15:17:17 +03:00
EXPORT_SYMBOL_GPL ( mtd_is_partition ) ;
2012-07-10 20:23:40 +04:00
/* 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 ;
2015-11-20 06:28:39 +03:00
return mtd_to_part ( mtd ) - > master - > size ;
2012-07-10 20:23:40 +04:00
}
EXPORT_SYMBOL_GPL ( mtd_get_device_size ) ;