2021-01-04 09:41:35 +05:30
// SPDX-License-Identifier: GPL-2.0-only
/*
* Qualcomm SMEM NAND flash partition parser
*
* Copyright ( C ) 2020 , Linaro Ltd .
*/
# include <linux/ctype.h>
# include <linux/module.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
# include <linux/slab.h>
# include <linux/soc/qcom/smem.h>
# define SMEM_AARM_PARTITION_TABLE 9
# define SMEM_APPS 0
# define SMEM_FLASH_PART_MAGIC1 0x55ee73aa
# define SMEM_FLASH_PART_MAGIC2 0xe35ebddb
# define SMEM_FLASH_PTABLE_V3 3
# define SMEM_FLASH_PTABLE_V4 4
# define SMEM_FLASH_PTABLE_MAX_PARTS_V3 16
# define SMEM_FLASH_PTABLE_MAX_PARTS_V4 48
# define SMEM_FLASH_PTABLE_HDR_LEN (4 * sizeof(u32))
# define SMEM_FLASH_PTABLE_NAME_SIZE 16
/**
* struct smem_flash_pentry - SMEM Flash partition entry
* @ name : Name of the partition
* @ offset : Offset in blocks
* @ length : Length of the partition in blocks
* @ attr : Flags for this partition
*/
struct smem_flash_pentry {
char name [ SMEM_FLASH_PTABLE_NAME_SIZE ] ;
__le32 offset ;
__le32 length ;
u8 attr ;
} __packed __aligned ( 4 ) ;
/**
* struct smem_flash_ptable - SMEM Flash partition table
* @ magic1 : Partition table Magic 1
* @ magic2 : Partition table Magic 2
* @ version : Partition table version
* @ numparts : Number of partitions in this ptable
* @ pentry : Flash partition entries belonging to this ptable
*/
struct smem_flash_ptable {
__le32 magic1 ;
__le32 magic2 ;
__le32 version ;
__le32 numparts ;
struct smem_flash_pentry pentry [ SMEM_FLASH_PTABLE_MAX_PARTS_V4 ] ;
} __packed __aligned ( 4 ) ;
static int parse_qcomsmem_part ( struct mtd_info * mtd ,
const struct mtd_partition * * pparts ,
struct mtd_part_parser_data * data )
{
2022-01-16 04:22:10 +01:00
size_t len = SMEM_FLASH_PTABLE_HDR_LEN ;
int ret , i , j , tmpparts , numparts = 0 ;
2021-01-04 09:41:35 +05:30
struct smem_flash_pentry * pentry ;
struct smem_flash_ptable * ptable ;
struct mtd_partition * parts ;
char * name , * c ;
2021-03-03 18:48:16 +02:00
if ( IS_ENABLED ( CONFIG_MTD_SPI_NOR_USE_4K_SECTORS )
& & mtd - > type = = MTD_NORFLASH ) {
pr_err ( " %s: SMEM partition parser is incompatible with 4K sectors \n " ,
mtd - > name ) ;
return - EINVAL ;
}
2021-01-04 09:41:35 +05:30
pr_debug ( " Parsing partition table info from SMEM \n " ) ;
ptable = qcom_smem_get ( SMEM_APPS , SMEM_AARM_PARTITION_TABLE , & len ) ;
if ( IS_ERR ( ptable ) ) {
2022-01-03 03:03:16 +00:00
if ( PTR_ERR ( ptable ) ! = - EPROBE_DEFER )
pr_err ( " Error reading partition table header \n " ) ;
2021-01-04 09:41:35 +05:30
return PTR_ERR ( ptable ) ;
}
/* Verify ptable magic */
if ( le32_to_cpu ( ptable - > magic1 ) ! = SMEM_FLASH_PART_MAGIC1 | |
le32_to_cpu ( ptable - > magic2 ) ! = SMEM_FLASH_PART_MAGIC2 ) {
pr_err ( " Partition table magic verification failed \n " ) ;
return - EINVAL ;
}
/* Ensure that # of partitions is less than the max we have allocated */
2022-01-16 04:22:10 +01:00
tmpparts = le32_to_cpu ( ptable - > numparts ) ;
if ( tmpparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4 ) {
2021-01-04 09:41:35 +05:30
pr_err ( " Partition numbers exceed the max limit \n " ) ;
return - EINVAL ;
}
/* Find out length of partition data based on table version */
if ( le32_to_cpu ( ptable - > version ) < = SMEM_FLASH_PTABLE_V3 ) {
len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 *
sizeof ( struct smem_flash_pentry ) ;
} else if ( le32_to_cpu ( ptable - > version ) = = SMEM_FLASH_PTABLE_V4 ) {
len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 *
sizeof ( struct smem_flash_pentry ) ;
} else {
pr_err ( " Unknown ptable version (%d) " , le32_to_cpu ( ptable - > version ) ) ;
return - EINVAL ;
}
/*
* Now that the partition table header has been parsed , verified
* and the length of the partition table calculated , read the
* complete partition table
*/
ptable = qcom_smem_get ( SMEM_APPS , SMEM_AARM_PARTITION_TABLE , & len ) ;
2021-03-03 09:46:34 +01:00
if ( IS_ERR ( ptable ) ) {
2021-01-04 09:41:35 +05:30
pr_err ( " Error reading partition table \n " ) ;
return PTR_ERR ( ptable ) ;
}
2022-01-16 04:22:10 +01:00
for ( i = 0 ; i < tmpparts ; i + + ) {
pentry = & ptable - > pentry [ i ] ;
if ( pentry - > name [ 0 ] ! = ' \0 ' )
numparts + + ;
}
2021-01-04 09:41:35 +05:30
parts = kcalloc ( numparts , sizeof ( * parts ) , GFP_KERNEL ) ;
if ( ! parts )
return - ENOMEM ;
2022-01-16 04:22:10 +01:00
for ( i = 0 , j = 0 ; i < tmpparts ; i + + ) {
2021-01-04 09:41:35 +05:30
pentry = & ptable - > pentry [ i ] ;
if ( pentry - > name [ 0 ] = = ' \0 ' )
continue ;
name = kstrdup ( pentry - > name , GFP_KERNEL ) ;
if ( ! name ) {
ret = - ENOMEM ;
goto out_free_parts ;
}
/* Convert name to lower case */
for ( c = name ; * c ! = ' \0 ' ; c + + )
* c = tolower ( * c ) ;
2022-01-16 04:22:10 +01:00
parts [ j ] . name = name ;
parts [ j ] . offset = le32_to_cpu ( pentry - > offset ) * mtd - > erasesize ;
parts [ j ] . mask_flags = pentry - > attr ;
parts [ j ] . size = le32_to_cpu ( pentry - > length ) * mtd - > erasesize ;
2021-01-04 09:41:35 +05:30
pr_debug ( " %d: %s offs=0x%08x size=0x%08x attr:0x%08x \n " ,
i , pentry - > name , le32_to_cpu ( pentry - > offset ) ,
le32_to_cpu ( pentry - > length ) , pentry - > attr ) ;
2022-01-16 04:22:10 +01:00
j + + ;
2021-01-04 09:41:35 +05:30
}
pr_debug ( " SMEM partition table found: ver: %d len: %d \n " ,
2022-01-16 04:22:10 +01:00
le32_to_cpu ( ptable - > version ) , tmpparts ) ;
2021-01-04 09:41:35 +05:30
* pparts = parts ;
return numparts ;
out_free_parts :
2022-01-16 04:22:10 +01:00
while ( - - j > = 0 )
kfree ( parts [ j ] . name ) ;
2021-01-04 09:41:35 +05:30
kfree ( parts ) ;
* pparts = NULL ;
return ret ;
}
2021-05-26 01:09:31 +02:00
static void parse_qcomsmem_cleanup ( const struct mtd_partition * pparts ,
int nr_parts )
{
int i ;
for ( i = 0 ; i < nr_parts ; i + + )
kfree ( pparts [ i ] . name ) ;
2022-01-16 04:22:11 +01:00
kfree ( pparts ) ;
2021-05-26 01:09:31 +02:00
}
2021-01-04 09:41:35 +05:30
static const struct of_device_id qcomsmem_of_match_table [ ] = {
{ . compatible = " qcom,smem-part " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , qcomsmem_of_match_table ) ;
static struct mtd_part_parser mtd_parser_qcomsmem = {
. parse_fn = parse_qcomsmem_part ,
2021-05-26 01:09:31 +02:00
. cleanup = parse_qcomsmem_cleanup ,
2021-01-04 09:41:35 +05:30
. name = " qcomsmem " ,
. of_match_table = qcomsmem_of_match_table ,
} ;
module_mtd_part_parser ( mtd_parser_qcomsmem ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Qualcomm SMEM NAND flash partition parser " ) ;