2019-05-23 12:14:39 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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-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>
2018-03-14 15:10:42 +03:00
# include <linux/of.h>
2022-05-10 16:12:59 +03:00
# include <linux/of_platform.h>
2005-04-17 02:20:36 +04:00
2011-05-23 13:23:42 +04:00
# include "mtdcore.h"
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 .
*/
2020-01-14 12:09:52 +03:00
static inline void free_partition ( struct mtd_info * mtd )
2010-09-17 14:31:41 +04:00
{
2020-01-14 12:09:52 +03:00
kfree ( mtd - > name ) ;
kfree ( mtd ) ;
2010-09-17 14:31:41 +04:00
}
2020-01-14 12:09:52 +03:00
static struct mtd_info * allocate_partition ( struct mtd_info * parent ,
const struct mtd_partition * part ,
int partno , uint64_t cur_offset )
2008-07-18 20:00:18 +04:00
{
2020-05-03 18:53:37 +03:00
struct mtd_info * master = mtd_get_master ( parent ) ;
int wr_alignment = ( parent - > flags & MTD_NO_ERASE ) ?
master - > writesize : master - > erasesize ;
u64 parent_size = mtd_is_partition ( parent ) ?
parent - > part . size : parent - > size ;
struct mtd_info * child ;
2017-06-09 06:58:31 +03:00
u32 remainder ;
2010-09-17 14:31:41 +04:00
char * name ;
2017-06-09 06:58:31 +03:00
u64 tmp ;
2008-07-18 20:00:18 +04:00
/* allocate the partition structure */
2020-01-14 12:09:52 +03:00
child = kzalloc ( sizeof ( * child ) , GFP_KERNEL ) ;
2010-09-17 14:31:41 +04:00
name = kstrdup ( part - > name , GFP_KERNEL ) ;
2020-01-14 12:09:52 +03:00
if ( ! name | | ! child ) {
2008-07-18 20:00:33 +04:00
printk ( KERN_ERR " memory allocation error while creating partitions for \" %s \" \n " ,
2017-06-21 09:26:44 +03:00
parent - > name ) ;
2010-09-17 14:31:41 +04:00
kfree ( name ) ;
2020-01-14 12:09:52 +03:00
kfree ( child ) ;
2010-09-17 14:31:41 +04:00
return ERR_PTR ( - ENOMEM ) ;
2008-07-18 20:00:18 +04:00
}
/* set up the MTD object for this partition */
2020-01-14 12:09:52 +03:00
child - > type = parent - > type ;
child - > part . flags = parent - > flags & ~ part - > mask_flags ;
2020-05-03 18:53:37 +03:00
child - > part . flags | = part - > add_flags ;
2020-01-14 12:09:52 +03:00
child - > flags = child - > part . flags ;
2020-05-03 18:53:37 +03:00
child - > part . size = part - > size ;
2020-01-14 12:09:52 +03:00
child - > writesize = parent - > writesize ;
child - > writebufsize = parent - > writebufsize ;
child - > oobsize = parent - > oobsize ;
child - > oobavail = parent - > oobavail ;
child - > subpage_sft = parent - > subpage_sft ;
child - > name = name ;
child - > owner = parent - > owner ;
2008-07-18 20:00:18 +04:00
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
2020-01-14 12:09:52 +03:00
* distinguish between the parent and its partitions in sysfs .
2009-03-26 10:42:41 +03:00
*/
2020-01-14 12:09:52 +03:00
child - > dev . parent = IS_ENABLED ( CONFIG_MTD_PARTITIONED_MASTER ) | | mtd_is_partition ( parent ) ?
& parent - > dev : parent - > dev . parent ;
child - > dev . of_node = part - > of_node ;
child - > parent = parent ;
child - > part . offset = part - > offset ;
INIT_LIST_HEAD ( & child - > partitions ) ;
if ( child - > part . offset = = MTDPART_OFS_APPEND )
child - > part . offset = cur_offset ;
if ( child - > part . offset = = MTDPART_OFS_NXTBLK ) {
2017-06-09 06:58:31 +03:00
tmp = cur_offset ;
2020-01-14 12:09:52 +03:00
child - > part . offset = cur_offset ;
2017-06-09 06:58:31 +03:00
remainder = do_div ( tmp , wr_alignment ) ;
if ( remainder ) {
2020-01-14 12:09:52 +03:00
child - > part . offset + = wr_alignment - remainder ;
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 ,
2020-01-14 12:09:52 +03:00
( unsigned long long ) cur_offset ,
child - > part . offset ) ;
2008-07-18 20:00:18 +04:00
}
}
2020-01-14 12:09:52 +03:00
if ( child - > part . offset = = MTDPART_OFS_RETAIN ) {
child - > part . offset = cur_offset ;
2020-05-03 18:53:37 +03:00
if ( parent_size - child - > part . offset > = child - > part . size ) {
child - > part . size = parent_size - child - > part . offset -
child - > part . size ;
2011-06-06 18:04:14 +04:00
} else {
printk ( KERN_ERR " mtd partition \" %s \" doesn't have enough space: %#llx < %#llx, disabled \n " ,
2020-05-03 18:53:37 +03:00
part - > name , parent_size - child - > part . offset ,
child - > part . size ) ;
2011-06-06 18:04:14 +04:00
/* register to preserve ordering */
goto out_register ;
}
}
2020-05-03 18:53:37 +03:00
if ( child - > part . size = = MTDPART_SIZ_FULL )
child - > part . size = parent_size - child - > part . offset ;
2008-07-18 20:00:18 +04:00
2020-01-14 12:09:52 +03:00
printk ( KERN_NOTICE " 0x%012llx-0x%012llx : \" %s \" \n " ,
2020-05-03 18:53:37 +03:00
child - > part . offset , child - > part . offset + child - > part . size ,
2020-01-14 12:09:52 +03:00
child - > name ) ;
2008-07-18 20:00:18 +04:00
/* let's do some sanity checks */
2020-05-03 18:53:37 +03:00
if ( child - > part . offset > = parent_size ) {
2008-07-18 20:01:22 +04:00
/* let's register it anyway to preserve ordering */
2020-01-14 12:09:52 +03:00
child - > part . offset = 0 ;
2020-05-03 18:53:37 +03:00
child - > part . size = 0 ;
2019-01-30 14:55:52 +03:00
/* Initialize ->erasesize to make add_mtd_device() happy. */
2020-01-14 12:09:52 +03:00
child - > erasesize = parent - > erasesize ;
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
}
2020-05-03 18:53:37 +03:00
if ( child - > part . offset + child - > part . size > parent - > size ) {
child - > part . size = parent_size - child - > part . 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 " ,
2020-05-03 18:53:37 +03:00
part - > name , parent - > name , child - > part . size ) ;
2008-07-18 20:00:18 +04:00
}
2020-05-03 18:53:37 +03:00
2017-06-21 09:26:44 +03:00
if ( parent - > numeraseregions > 1 ) {
2008-07-18 20:00:18 +04:00
/* Deal with variable erase size stuff */
2017-06-21 09:26:44 +03:00
int i , max = parent - > numeraseregions ;
2020-05-03 18:53:37 +03:00
u64 end = child - > part . offset + child - > part . size ;
2017-06-21 09:26:44 +03:00
struct mtd_erase_region_info * regions = parent - > eraseregions ;
2008-07-18 20:00:18 +04:00
2008-07-18 20:00:57 +04:00
/* Find the first erase regions which is part of this
* partition . */
2020-01-14 12:09:52 +03:00
for ( i = 0 ; i < max & & regions [ i ] . offset < = child - > part . 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 + + ) {
2020-01-14 12:09:52 +03:00
if ( child - > erasesize < regions [ i ] . erasesize )
child - > erasesize = regions [ i ] . erasesize ;
2008-07-18 20:00:18 +04:00
}
2020-01-14 12:09:52 +03:00
BUG_ON ( child - > erasesize = = 0 ) ;
2008-07-18 20:00:18 +04:00
} else {
/* Single erase size */
2020-05-03 18:53:37 +03:00
child - > erasesize = master - > erasesize ;
2008-07-18 20:00:18 +04:00
}
2017-09-25 11:19:57 +03:00
/*
2020-01-14 12:09:52 +03:00
* Child erasesize might differ from the parent one if the parent
2017-09-25 11:19:57 +03:00
* exposes several regions with different erasesize . Adjust
* wr_alignment accordingly .
*/
2020-01-14 12:09:52 +03:00
if ( ! ( child - > flags & MTD_NO_ERASE ) )
wr_alignment = child - > erasesize ;
2017-09-25 11:19:57 +03:00
2020-01-14 12:09:52 +03:00
tmp = mtd_get_master_ofs ( child , 0 ) ;
2017-06-09 06:58:31 +03:00
remainder = do_div ( tmp , wr_alignment ) ;
2020-01-14 12:09:52 +03:00
if ( ( child - > flags & MTD_WRITEABLE ) & & remainder ) {
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 */
2020-01-14 12:09:52 +03:00
child - > flags & = ~ MTD_WRITEABLE ;
2017-06-09 06:58:31 +03:00
printk ( KERN_WARNING " mtd: partition \" %s \" doesn't start on an erase/write block boundary -- force read-only \n " ,
2008-07-18 20:00:18 +04:00
part - > name ) ;
}
2017-06-09 06:58:31 +03:00
2020-05-03 18:53:37 +03:00
tmp = mtd_get_master_ofs ( child , 0 ) + child - > part . size ;
2017-06-09 06:58:31 +03:00
remainder = do_div ( tmp , wr_alignment ) ;
2020-01-14 12:09:52 +03:00
if ( ( child - > flags & MTD_WRITEABLE ) & & remainder ) {
child - > flags & = ~ MTD_WRITEABLE ;
2017-06-09 06:58:31 +03:00
printk ( KERN_WARNING " mtd: partition \" %s \" doesn't end on an erase/write block -- force read-only \n " ,
2008-07-18 20:00:18 +04:00
part - > name ) ;
}
2020-05-03 18:53:37 +03:00
child - > size = child - > part . size ;
2020-01-14 12:09:52 +03:00
child - > ecc_step_size = parent - > ecc_step_size ;
child - > ecc_strength = parent - > ecc_strength ;
child - > bitflip_threshold = parent - > bitflip_threshold ;
2012-04-25 23:06:08 +04:00
2020-01-14 12:09:52 +03: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
2020-05-03 18:53:37 +03:00
while ( offs < child - > part . size ) {
2020-01-14 12:09:52 +03:00
if ( mtd_block_isreserved ( child , offs ) )
child - > ecc_stats . bbtblocks + + ;
else if ( mtd_block_isbad ( child , offs ) )
child - > ecc_stats . badblocks + + ;
offs + = child - > erasesize ;
2008-07-18 20:00:18 +04:00
}
}
2008-07-18 20:01:22 +04:00
out_register :
2020-01-14 12:09:52 +03:00
return child ;
2008-07-18 20:00:18 +04:00
}
2021-06-03 15:30:41 +03:00
static ssize_t offset_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2015-04-03 01:15:11 +03:00
{
struct mtd_info * mtd = dev_get_drvdata ( dev ) ;
2020-01-14 12:09:52 +03:00
2021-04-12 12:35:44 +03:00
return sysfs_emit ( buf , " %lld \n " , mtd - > part . offset ) ;
2015-04-03 01:15:11 +03:00
}
2021-06-03 15:30:41 +03:00
static DEVICE_ATTR_RO ( offset ) ; /* mtd partition offset */
2015-04-03 01:15:11 +03:00
static const struct attribute * mtd_partition_attrs [ ] = {
& dev_attr_offset . attr ,
NULL
} ;
2020-01-14 12:09:52 +03:00
static int mtd_add_partition_attrs ( struct mtd_info * new )
2015-04-03 01:15:11 +03:00
{
2020-01-14 12:09:52 +03:00
int ret = sysfs_create_files ( & new - > dev . kobj , mtd_partition_attrs ) ;
2015-04-03 01:15:11 +03:00
if ( ret )
printk ( KERN_WARNING
" mtd: failed to create partition attrs, err=%d \n " , ret ) ;
return ret ;
}
2017-06-21 09:26:44 +03:00
int mtd_add_partition ( struct mtd_info * parent , const char * name ,
2010-09-17 14:31:41 +04:00
long long offset , long long length )
{
2020-01-14 12:09:52 +03:00
struct mtd_info * master = mtd_get_master ( parent ) ;
2020-05-03 18:53:37 +03:00
u64 parent_size = mtd_is_partition ( parent ) ?
parent - > part . size : parent - > size ;
2010-09-17 14:31:41 +04:00
struct mtd_partition part ;
2020-01-14 12:09:52 +03:00
struct mtd_info * child ;
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 )
2020-05-03 18:53:37 +03:00
length = parent_size - offset ;
2010-09-17 14:31:41 +04:00
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 ;
2020-01-14 12:09:52 +03:00
child = allocate_partition ( parent , & part , - 1 , offset ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2010-09-17 14:31:41 +04:00
2020-01-14 12:09:52 +03:00
mutex_lock ( & master - > master . partitions_lock ) ;
list_add_tail ( & child - > part . node , & parent - > partitions ) ;
mutex_unlock ( & master - > master . partitions_lock ) ;
2010-09-17 14:31:41 +04:00
2020-01-14 12:09:52 +03:00
ret = add_mtd_device ( child ) ;
2019-01-02 17:36:54 +03:00
if ( ret )
goto err_remove_part ;
2010-09-17 14:31:41 +04:00
2020-01-14 12:09:52 +03:00
mtd_add_partition_attrs ( child ) ;
2015-04-03 01:15:11 +03:00
2019-01-02 17:36:54 +03:00
return 0 ;
err_remove_part :
2020-01-14 12:09:52 +03:00
mutex_lock ( & master - > master . partitions_lock ) ;
list_del ( & child - > part . node ) ;
mutex_unlock ( & master - > master . partitions_lock ) ;
2019-01-02 17:36:54 +03:00
2020-01-14 12:09:52 +03:00
free_partition ( child ) ;
2019-01-02 17:36:54 +03:00
2010-09-17 14:31:41 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( mtd_add_partition ) ;
2017-06-21 09:26:42 +03:00
/**
* __mtd_del_partition - delete MTD partition
*
2020-11-09 21:21:44 +03:00
* @ mtd : MTD structure to be deleted
2017-06-21 09:26:42 +03:00
*
* This function must be called with the partitions mutex locked .
*/
2020-01-14 12:09:52 +03:00
static int __mtd_del_partition ( struct mtd_info * mtd )
2017-06-21 09:26:42 +03:00
{
2020-01-14 12:09:52 +03:00
struct mtd_info * child , * next ;
2017-06-21 09:26:42 +03:00
int err ;
2020-01-14 12:09:52 +03:00
list_for_each_entry_safe ( child , next , & mtd - > partitions , part . node ) {
err = __mtd_del_partition ( child ) ;
if ( err )
return err ;
2017-06-21 09:26:45 +03:00
}
2020-01-14 12:09:52 +03:00
sysfs_remove_files ( & mtd - > dev . kobj , mtd_partition_attrs ) ;
2017-06-21 09:26:43 +03:00
2020-01-14 12:09:52 +03:00
err = del_mtd_device ( mtd ) ;
2017-06-21 09:26:42 +03:00
if ( err )
return err ;
2021-11-02 20:26:04 +03:00
list_del ( & mtd - > part . node ) ;
2020-01-14 12:09:52 +03:00
free_partition ( mtd ) ;
2017-06-21 09:26:42 +03:00
return 0 ;
}
/*
* This function unregisters and destroy all slave MTD objects which are
2020-01-14 12:09:52 +03:00
* attached to the given MTD object , recursively .
2017-06-21 09:26:42 +03:00
*/
2020-01-14 12:09:52 +03:00
static int __del_mtd_partitions ( struct mtd_info * mtd )
2017-06-21 09:26:42 +03:00
{
2020-01-14 12:09:52 +03:00
struct mtd_info * child , * next ;
LIST_HEAD ( tmp_list ) ;
2017-06-21 09:26:42 +03:00
int ret , err = 0 ;
2020-01-14 12:09:52 +03:00
list_for_each_entry_safe ( child , next , & mtd - > partitions , part . node ) {
if ( mtd_has_partitions ( child ) )
2021-02-17 22:53:20 +03:00
__del_mtd_partitions ( child ) ;
2020-01-14 12:09:52 +03:00
pr_info ( " Deleting %s MTD partition \n " , child - > name ) ;
ret = del_mtd_device ( child ) ;
if ( ret < 0 ) {
pr_err ( " Error when deleting partition \" %s \" (%d) \n " ,
child - > name , ret ) ;
err = ret ;
continue ;
2017-06-21 09:26:42 +03:00
}
2020-01-14 12:09:52 +03:00
list_del ( & child - > part . node ) ;
free_partition ( child ) ;
}
2017-06-21 09:26:42 +03:00
return err ;
}
2020-01-14 12:09:52 +03:00
int del_mtd_partitions ( struct mtd_info * mtd )
{
struct mtd_info * master = mtd_get_master ( mtd ) ;
int ret ;
pr_info ( " Deleting MTD partitions on \" %s \" : \n " , mtd - > name ) ;
mutex_lock ( & master - > master . partitions_lock ) ;
ret = __del_mtd_partitions ( mtd ) ;
mutex_unlock ( & master - > master . partitions_lock ) ;
return ret ;
}
2017-06-21 09:26:45 +03:00
int mtd_del_partition ( struct mtd_info * mtd , int partno )
2010-09-17 14:31:41 +04:00
{
2020-01-14 12:09:52 +03:00
struct mtd_info * child , * master = mtd_get_master ( mtd ) ;
2010-09-17 14:31:41 +04:00
int ret = - EINVAL ;
2020-01-14 12:09:52 +03:00
mutex_lock ( & master - > master . partitions_lock ) ;
list_for_each_entry ( child , & mtd - > partitions , part . node ) {
if ( child - > index = = partno ) {
ret = __mtd_del_partition ( child ) ;
2010-09-17 14:31:41 +04:00
break ;
}
2020-01-14 12:09:52 +03:00
}
mutex_unlock ( & master - > master . partitions_lock ) ;
2010-09-17 14:31:41 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( mtd_del_partition ) ;
2005-04-17 02:20:36 +04:00
/*
2020-01-14 12:09:52 +03:00
* This function , given a parent MTD object and a partition table , creates
* and registers the child MTD objects which are bound to the parent according
* to the partition definitions .
2009-03-26 10:42:41 +03:00
*
2020-01-14 12:09:52 +03:00
* For historical reasons , this function ' s caller only registers the parent
2015-04-03 01:15:10 +03:00
* if the MTD_PARTITIONED_MASTER config option is set .
2005-04-17 02:20:36 +04:00
*/
2020-01-14 12:09:52 +03:00
int add_mtd_partitions ( struct mtd_info * parent ,
2005-04-17 02:20:36 +04:00
const struct mtd_partition * parts ,
int nbparts )
{
2020-01-14 12:09:52 +03:00
struct mtd_info * child , * master = mtd_get_master ( parent ) ;
2008-12-10 16:37:21 +03:00
uint64_t cur_offset = 0 ;
2019-01-02 17:36:54 +03:00
int i , ret ;
2005-04-17 02:20:36 +04:00
2020-01-14 12:09:52 +03:00
printk ( KERN_NOTICE " Creating %d MTD partitions on \" %s \" : \n " ,
nbparts , parent - > name ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < nbparts ; i + + ) {
2020-01-14 12:09:52 +03:00
child = allocate_partition ( parent , parts + i , i , cur_offset ) ;
if ( IS_ERR ( child ) ) {
ret = PTR_ERR ( child ) ;
2019-01-02 17:36:54 +03:00
goto err_del_partitions ;
2015-07-30 13:18:03 +03:00
}
2010-09-17 14:31:41 +04:00
2020-01-14 12:09:52 +03:00
mutex_lock ( & master - > master . partitions_lock ) ;
list_add_tail ( & child - > part . node , & parent - > partitions ) ;
mutex_unlock ( & master - > master . partitions_lock ) ;
2010-09-17 14:31:41 +04:00
2020-01-14 12:09:52 +03:00
ret = add_mtd_device ( child ) ;
2019-01-02 17:36:54 +03:00
if ( ret ) {
2020-01-14 12:09:52 +03:00
mutex_lock ( & master - > master . partitions_lock ) ;
list_del ( & child - > part . node ) ;
mutex_unlock ( & master - > master . partitions_lock ) ;
2019-01-02 17:36:54 +03:00
2020-01-14 12:09:52 +03:00
free_partition ( child ) ;
2019-01-02 17:36:54 +03:00
goto err_del_partitions ;
}
2020-01-14 12:09:52 +03:00
mtd_add_partition_attrs ( child ) ;
2018-07-13 17:32:21 +03:00
/* Look for subpartitions */
2020-01-14 12:09:52 +03:00
parse_mtd_partitions ( child , parts [ i ] . types , NULL ) ;
2010-09-17 14:31:41 +04:00
2020-05-03 18:53:37 +03:00
cur_offset = child - > part . offset + child - > part . size ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
2019-01-02 17:36:54 +03:00
err_del_partitions :
del_mtd_partitions ( master ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
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-12-09 21:24:03 +03:00
/*
* Many partition parsers just expected the core to kfree ( ) all their data in
* one chunk . Do that by default .
*/
static void mtd_part_parser_cleanup_default ( const struct mtd_partition * pparts ,
int nr_parts )
{
kfree ( pparts ) ;
}
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 ;
2015-12-09 21:24:03 +03:00
if ( ! p - > cleanup )
p - > cleanup = & mtd_part_parser_cleanup_default ;
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
2018-07-13 17:32:21 +03:00
/* Check DT only when looking for subpartitions. */
static const char * const default_subpartition_types [ ] = {
" ofpart " ,
NULL
} ;
2017-05-23 08:30:20 +03:00
static int mtd_part_do_parse ( struct mtd_part_parser * parser ,
struct mtd_info * master ,
struct mtd_partitions * pparts ,
struct mtd_part_parser_data * data )
{
int ret ;
ret = ( * parser - > parse_fn ) ( master , & pparts - > parts , data ) ;
pr_debug ( " %s: parser %s: %i \n " , master - > name , parser - > name , ret ) ;
if ( ret < = 0 )
return ret ;
pr_notice ( " %d %s partitions found on MTD device %s \n " , ret ,
parser - > name , master - > name ) ;
pparts - > nr_parts = ret ;
pparts - > parser = parser ;
return ret ;
}
2018-03-14 15:10:42 +03:00
/**
* mtd_part_get_compatible_parser - find MTD parser by a compatible string
*
* @ compat : compatible string describing partitions in a device tree
*
* MTD parsers can specify supported partitions by providing a table of
* compatibility strings . This function finds a parser that advertises support
* for a passed value of " compatible " .
*/
static struct mtd_part_parser * mtd_part_get_compatible_parser ( const char * compat )
{
struct mtd_part_parser * p , * ret = NULL ;
spin_lock ( & part_parser_lock ) ;
list_for_each_entry ( p , & part_parsers , list ) {
const struct of_device_id * matches ;
matches = p - > of_match_table ;
if ( ! matches )
continue ;
for ( ; matches - > compatible [ 0 ] ; matches + + ) {
if ( ! strcmp ( matches - > compatible , compat ) & &
try_module_get ( p - > owner ) ) {
ret = p ;
break ;
}
}
if ( ret )
break ;
}
spin_unlock ( & part_parser_lock ) ;
return ret ;
}
static int mtd_part_of_parse ( struct mtd_info * master ,
struct mtd_partitions * pparts )
{
struct mtd_part_parser * parser ;
struct device_node * np ;
2023-02-07 04:42:04 +03:00
struct device_node * child ;
2018-03-14 15:10:42 +03:00
struct property * prop ;
2022-05-10 16:12:59 +03:00
struct device * dev ;
2018-03-14 15:10:42 +03:00
const char * compat ;
2018-03-14 15:10:43 +03:00
const char * fixed = " fixed-partitions " ;
2018-03-14 15:10:42 +03:00
int ret , err = 0 ;
2022-05-10 16:12:59 +03:00
dev = & master - > dev ;
/* Use parent device (controller) if the top level MTD is not registered */
if ( ! IS_ENABLED ( CONFIG_MTD_PARTITIONED_MASTER ) & & ! mtd_is_partition ( master ) )
dev = master - > dev . parent ;
2018-07-13 17:32:21 +03:00
np = mtd_get_of_node ( master ) ;
2018-09-07 17:35:54 +03:00
if ( mtd_is_partition ( master ) )
of_node_get ( np ) ;
else
2018-07-13 17:32:21 +03:00
np = of_get_child_by_name ( np , " partitions " ) ;
2018-09-07 17:35:54 +03:00
2023-02-07 04:42:04 +03:00
/*
* Don ' t create devices that are added to a bus but will never get
* probed . That ' ll cause fw_devlink to block probing of consumers of
* this partition until the partition device is probed .
*/
for_each_child_of_node ( np , child )
if ( of_device_is_compatible ( child , " nvmem-cells " ) )
of_node_set_flag ( child , OF_POPULATED ) ;
2018-03-14 15:10:42 +03:00
of_property_for_each_string ( np , " compatible " , prop , compat ) {
parser = mtd_part_get_compatible_parser ( compat ) ;
if ( ! parser )
continue ;
ret = mtd_part_do_parse ( parser , master , pparts , NULL ) ;
if ( ret > 0 ) {
2022-05-10 16:12:59 +03:00
of_platform_populate ( np , NULL , NULL , dev ) ;
2018-03-14 15:10:42 +03:00
of_node_put ( np ) ;
return ret ;
}
mtd_part_parser_put ( parser ) ;
if ( ret < 0 & & ! err )
err = ret ;
}
2022-05-10 16:12:59 +03:00
of_platform_populate ( np , NULL , NULL , dev ) ;
2018-03-14 15:10:42 +03:00
of_node_put ( np ) ;
/*
2018-03-14 15:10:43 +03:00
* For backward compatibility we have to try the " fixed-partitions "
2018-03-14 15:10:42 +03:00
* parser . It supports old DT format with partitions specified as a
* direct subnodes of a flash device DT node without any compatibility
* specified we could match .
*/
parser = mtd_part_parser_get ( fixed ) ;
if ( ! parser & & ! request_module ( " %s " , fixed ) )
parser = mtd_part_parser_get ( fixed ) ;
if ( parser ) {
ret = mtd_part_do_parse ( parser , master , pparts , NULL ) ;
if ( ret > 0 )
return ret ;
mtd_part_parser_put ( parser ) ;
if ( ret < 0 & & ! err )
err = ret ;
}
return err ;
}
2011-06-08 12:42:27 +04:00
/**
2018-03-27 23:35:41 +03:00
* parse_mtd_partitions - parse and register MTD partitions
*
2011-06-08 12:42:27 +04:00
* @ master : the master partition ( describes whole MTD device )
* @ types : names of partition parsers to try or % NULL
2011-06-10 18:18:28 +04:00
* @ data : MTD partition parser - specific data
2011-06-08 12:42:27 +04:00
*
2018-03-27 23:35:41 +03:00
* This function tries to find & register partitions 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
2018-03-27 23:35:41 +03:00
* o number of found partitions otherwise
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 ,
2011-06-10 18:18:28 +04:00
struct mtd_part_parser_data * data )
2005-04-17 02:20:36 +04:00
{
2018-03-27 23:35:41 +03:00
struct mtd_partitions pparts = { } ;
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 )
2018-07-13 17:32:21 +03:00
types = mtd_is_partition ( master ) ? default_subpartition_types :
default_mtd_part_types ;
2011-06-02 18:51:16 +04:00
2015-10-11 23:03:47 +03:00
for ( ; * types ; types + + ) {
2018-03-14 15:10:42 +03:00
/*
* ofpart is a special type that means OF partitioning info
* should be used . It requires a bit different logic so it is
* handled in a separated function .
*/
if ( ! strcmp ( * types , " ofpart " ) ) {
2018-03-27 23:35:41 +03:00
ret = mtd_part_of_parse ( master , & pparts ) ;
2018-03-14 15:10:42 +03:00
} else {
pr_debug ( " %s: parsing partitions %s \n " , master - > name ,
* types ) ;
2015-12-05 02:25:15 +03:00
parser = mtd_part_parser_get ( * types ) ;
2018-03-14 15:10:42 +03:00
if ( ! parser & & ! request_module ( " %s " , * types ) )
parser = mtd_part_parser_get ( * types ) ;
pr_debug ( " %s: got parser %s \n " , master - > name ,
parser ? parser - > name : NULL ) ;
if ( ! parser )
continue ;
2018-03-27 23:35:41 +03:00
ret = mtd_part_do_parse ( parser , master , & pparts , data ) ;
2018-03-14 15:10:42 +03:00
if ( ret < = 0 )
mtd_part_parser_put ( parser ) ;
}
2017-05-23 08:30:20 +03:00
/* Found partitions! */
2018-03-27 23:35:41 +03:00
if ( ret > 0 ) {
err = add_mtd_partitions ( master , pparts . parts ,
pparts . nr_parts ) ;
mtd_part_parser_cleanup ( & pparts ) ;
return err ? err : pparts . nr_parts ;
}
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
2015-12-09 21:24:03 +03:00
void mtd_part_parser_cleanup ( struct mtd_partitions * parts )
{
const struct mtd_part_parser * parser ;
if ( ! parts )
return ;
parser = parts - > parser ;
if ( parser ) {
if ( parser - > cleanup )
parser - > cleanup ( parts - > parts , parts - > nr_parts ) ;
mtd_part_parser_put ( parser ) ;
}
}
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 )
{
2020-01-14 12:09:52 +03:00
struct mtd_info * master = mtd_get_master ( ( struct mtd_info * ) mtd ) ;
2012-07-10 20:23:40 +04:00
2020-01-14 12:09:52 +03:00
return master - > size ;
2012-07-10 20:23:40 +04:00
}
EXPORT_SYMBOL_GPL ( mtd_get_device_size ) ;