2020-06-22 22:19:39 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( c ) 2019 - 2020 Linaro Ltd .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of_address.h>
# include "qcom_pil_info.h"
/*
* The PIL relocation information region is used to communicate memory regions
* occupied by co - processor firmware for post mortem crash analysis .
*
* It consists of an array of entries with an 8 byte textual identifier of the
* region followed by a 64 bit base address and 32 bit size , both little
* endian .
*/
# define PIL_RELOC_NAME_LEN 8
# define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))
struct pil_reloc {
void __iomem * base ;
size_t num_entries ;
} ;
static struct pil_reloc _reloc __read_mostly ;
static DEFINE_MUTEX ( pil_reloc_lock ) ;
static int qcom_pil_info_init ( void )
{
struct device_node * np ;
struct resource imem ;
void __iomem * base ;
int ret ;
/* Already initialized? */
if ( _reloc . base )
return 0 ;
np = of_find_compatible_node ( NULL , NULL , " qcom,pil-reloc-info " ) ;
if ( ! np )
return - ENOENT ;
ret = of_address_to_resource ( np , 0 , & imem ) ;
of_node_put ( np ) ;
if ( ret < 0 )
return ret ;
base = ioremap ( imem . start , resource_size ( & imem ) ) ;
if ( ! base ) {
pr_err ( " failed to map PIL relocation info region \n " ) ;
return - ENOMEM ;
}
memset_io ( base , 0 , resource_size ( & imem ) ) ;
_reloc . base = base ;
2021-01-03 16:56:12 +03:00
_reloc . num_entries = ( u32 ) resource_size ( & imem ) / PIL_RELOC_ENTRY_SIZE ;
2020-06-22 22:19:39 +03:00
return 0 ;
}
/**
* qcom_pil_info_store ( ) - store PIL information of image in IMEM
* @ image : name of the image
* @ base : base address of the loaded image
* @ size : size of the loaded image
*
* Return : 0 on success , negative errno on failure
*/
int qcom_pil_info_store ( const char * image , phys_addr_t base , size_t size )
{
char buf [ PIL_RELOC_NAME_LEN ] ;
void __iomem * entry ;
int ret ;
int i ;
mutex_lock ( & pil_reloc_lock ) ;
ret = qcom_pil_info_init ( ) ;
if ( ret < 0 ) {
mutex_unlock ( & pil_reloc_lock ) ;
return ret ;
}
for ( i = 0 ; i < _reloc . num_entries ; i + + ) {
entry = _reloc . base + i * PIL_RELOC_ENTRY_SIZE ;
memcpy_fromio ( buf , entry , PIL_RELOC_NAME_LEN ) ;
/*
* An empty record means we didn ' t find it , given that the
* records are packed .
*/
if ( ! buf [ 0 ] )
goto found_unused ;
if ( ! strncmp ( buf , image , PIL_RELOC_NAME_LEN ) )
goto found_existing ;
}
pr_warn ( " insufficient PIL info slots \n " ) ;
mutex_unlock ( & pil_reloc_lock ) ;
return - ENOMEM ;
found_unused :
memcpy_toio ( entry , image , PIL_RELOC_NAME_LEN ) ;
found_existing :
/* Use two writel() as base is only aligned to 4 bytes on odd entries */
writel ( base , entry + PIL_RELOC_NAME_LEN ) ;
2020-07-16 08:48:17 +03:00
writel ( ( u64 ) base > > 32 , entry + PIL_RELOC_NAME_LEN + 4 ) ;
2020-06-22 22:19:39 +03:00
writel ( size , entry + PIL_RELOC_NAME_LEN + sizeof ( __le64 ) ) ;
mutex_unlock ( & pil_reloc_lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( qcom_pil_info_store ) ;
static void __exit pil_reloc_exit ( void )
{
mutex_lock ( & pil_reloc_lock ) ;
iounmap ( _reloc . base ) ;
_reloc . base = NULL ;
mutex_unlock ( & pil_reloc_lock ) ;
}
module_exit ( pil_reloc_exit ) ;
MODULE_DESCRIPTION ( " Qualcomm PIL relocation info " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;