@ -35,6 +35,7 @@
# include <linux/debugfs.h>
# include <linux/remoteproc.h>
# include <linux/iommu.h>
# include <linux/idr.h>
# include <linux/klist.h>
# include <linux/elf.h>
# include <linux/virtio_ids.h>
@ -66,6 +67,9 @@ typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
struct resource_table * table , int len ) ;
typedef int ( * rproc_handle_resource_t ) ( struct rproc * rproc , void * , int avail ) ;
/* Unique indices for remoteproc devices */
static DEFINE_IDA ( rproc_dev_index ) ;
/*
* This is the IOMMU fault handler we register with the IOMMU API
* ( when relevant ; not all remote processors access memory through
@ -92,7 +96,7 @@ static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
static int rproc_enable_iommu ( struct rproc * rproc )
{
struct iommu_domain * domain ;
struct device * dev = rproc - > dev ;
struct device * dev = rproc - > dev . parent ;
int ret ;
/*
@ -137,7 +141,7 @@ free_domain:
static void rproc_disable_iommu ( struct rproc * rproc )
{
struct iommu_domain * domain = rproc - > domain ;
struct device * dev = rproc - > dev ;
struct device * dev = rproc - > dev . parent ;
if ( ! domain )
return ;
@ -217,7 +221,7 @@ static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
static int
rproc_load_segments ( struct rproc * rproc , const u8 * elf_data , size_t len )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct elf32_hdr * ehdr ;
struct elf32_phdr * phdr ;
int i , ret = 0 ;
@ -282,7 +286,7 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
int rproc_alloc_vring ( struct rproc_vdev * rvdev , int i )
{
struct rproc * rproc = rvdev - > rproc ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct rproc_vring * rvring = & rvdev - > vring [ i ] ;
dma_addr_t dma ;
void * va ;
@ -301,9 +305,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
* this call will also configure the IOMMU for us
* TODO : let the rproc know the da of this vring
*/
va = dma_alloc_coherent ( dev , size , & dma , GFP_KERNEL ) ;
va = dma_alloc_coherent ( dev - > parent , size , & dma , GFP_KERNEL ) ;
if ( ! va ) {
dev_err ( dev , " dma_alloc_coherent failed \n " ) ;
dev_err ( dev - > parent , " dma_alloc_coherent failed \n " ) ;
return - EINVAL ;
}
@ -316,7 +320,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
ret = idr_get_new ( & rproc - > notifyids , rvring , & notifyid ) ;
if ( ret ) {
dev_err ( dev , " idr_get_new failed: %d \n " , ret ) ;
dma_free_coherent ( dev , size , va , dma ) ;
dma_free_coherent ( dev - > parent , size , va , dma ) ;
return ret ;
}
@ -334,7 +338,7 @@ static int
rproc_parse_vring ( struct rproc_vdev * rvdev , struct fw_rsc_vdev * rsc , int i )
{
struct rproc * rproc = rvdev - > rproc ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct fw_rsc_vdev_vring * vring = & rsc - > vring [ i ] ;
struct rproc_vring * rvring = & rvdev - > vring [ i ] ;
@ -366,7 +370,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
int size = PAGE_ALIGN ( vring_size ( rvring - > len , rvring - > align ) ) ;
struct rproc * rproc = rvring - > rvdev - > rproc ;
dma_free_coherent ( rproc - > dev , size , rvring - > va , rvring - > dma ) ;
dma_free_coherent ( rproc - > dev . parent , size , rvring - > va , rvring - > dma ) ;
idr_remove ( & rproc - > notifyids , rvring - > notifyid ) ;
}
@ -400,14 +404,14 @@ void rproc_free_vring(struct rproc_vring *rvring)
static int rproc_handle_vdev ( struct rproc * rproc , struct fw_rsc_vdev * rsc ,
int avail )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct rproc_vdev * rvdev ;
int i , ret ;
/* make sure resource isn't truncated */
if ( sizeof ( * rsc ) + rsc - > num_of_vrings * sizeof ( struct fw_rsc_vdev_vring )
+ rsc - > config_len > avail ) {
dev_err ( rproc - > dev , " vdev rsc is truncated \n " ) ;
dev_err ( dev , " vdev rsc is truncated \n " ) ;
return - EINVAL ;
}
@ -476,12 +480,12 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
int avail )
{
struct rproc_mem_entry * trace ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
void * ptr ;
char name [ 15 ] ;
if ( sizeof ( * rsc ) > avail ) {
dev_err ( rproc - > dev , " trace rsc is truncated \n " ) ;
dev_err ( dev , " trace rsc is truncated \n " ) ;
return - EINVAL ;
}
@ -558,6 +562,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
int avail )
{
struct rproc_mem_entry * mapping ;
struct device * dev = & rproc - > dev ;
int ret ;
/* no point in handling this resource without a valid iommu domain */
@ -565,25 +570,25 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
return - EINVAL ;
if ( sizeof ( * rsc ) > avail ) {
dev_err ( rproc - > dev , " devmem rsc is truncated \n " ) ;
dev_err ( dev , " devmem rsc is truncated \n " ) ;
return - EINVAL ;
}
/* make sure reserved bytes are zeroes */
if ( rsc - > reserved ) {
dev_err ( rproc - > dev , " devmem rsc has non zero reserved bytes \n " ) ;
dev_err ( dev , " devmem rsc has non zero reserved bytes \n " ) ;
return - EINVAL ;
}
mapping = kzalloc ( sizeof ( * mapping ) , GFP_KERNEL ) ;
if ( ! mapping ) {
dev_err ( rproc - > dev , " kzalloc mapping failed \n " ) ;
dev_err ( dev , " kzalloc mapping failed \n " ) ;
return - ENOMEM ;
}
ret = iommu_map ( rproc - > domain , rsc - > da , rsc - > pa , rsc - > len , rsc - > flags ) ;
if ( ret ) {
dev_err ( rproc - > dev , " failed to map devmem: %d \n " , ret ) ;
dev_err ( dev , " failed to map devmem: %d \n " , ret ) ;
goto out ;
}
@ -598,7 +603,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
mapping - > len = rsc - > len ;
list_add_tail ( & mapping - > node , & rproc - > mappings ) ;
dev_dbg ( rproc - > dev , " mapped devmem pa 0x%x, da 0x%x, len 0x%x \n " ,
dev_dbg ( dev , " mapped devmem pa 0x%x, da 0x%x, len 0x%x \n " ,
rsc - > pa , rsc - > da , rsc - > len ) ;
return 0 ;
@ -630,13 +635,13 @@ static int rproc_handle_carveout(struct rproc *rproc,
struct fw_rsc_carveout * rsc , int avail )
{
struct rproc_mem_entry * carveout , * mapping ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
dma_addr_t dma ;
void * va ;
int ret ;
if ( sizeof ( * rsc ) > avail ) {
dev_err ( rproc - > dev , " carveout rsc is truncated \n " ) ;
dev_err ( dev , " carveout rsc is truncated \n " ) ;
return - EINVAL ;
}
@ -662,9 +667,9 @@ static int rproc_handle_carveout(struct rproc *rproc,
goto free_mapping ;
}
va = dma_alloc_coherent ( dev , rsc - > len , & dma , GFP_KERNEL ) ;
va = dma_alloc_coherent ( dev - > parent , rsc - > len , & dma , GFP_KERNEL ) ;
if ( ! va ) {
dev_err ( dev , " failed to dma alloc carveout : %d\n " , rsc - > len ) ;
dev_err ( dev - > parent , " dma_alloc_coherent err : %d\n " , rsc - > len ) ;
ret = - ENOMEM ;
goto free_carv ;
}
@ -735,7 +740,7 @@ static int rproc_handle_carveout(struct rproc *rproc,
return 0 ;
dma_free :
dma_free_coherent ( dev , rsc - > len , va , dma ) ;
dma_free_coherent ( dev - > parent , rsc - > len , va , dma ) ;
free_carv :
kfree ( carveout ) ;
free_mapping :
@ -758,7 +763,7 @@ static rproc_handle_resource_t rproc_handle_rsc[] = {
static int
rproc_handle_boot_rsc ( struct rproc * rproc , struct resource_table * table , int len )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
rproc_handle_resource_t handler ;
int ret = 0 , i ;
@ -797,7 +802,7 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len
static int
rproc_handle_virtio_rsc ( struct rproc * rproc , struct resource_table * table , int len )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
int ret = 0 , i ;
for ( i = 0 ; i < table - > num ; i + + ) {
@ -850,7 +855,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
struct elf32_hdr * ehdr ;
struct elf32_shdr * shdr ;
const char * name_table ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct resource_table * table = NULL ;
int i ;
@ -916,7 +921,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
static void rproc_resource_cleanup ( struct rproc * rproc )
{
struct rproc_mem_entry * entry , * tmp ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
/* clean up debugfs trace entries */
list_for_each_entry_safe ( entry , tmp , & rproc - > traces , node ) {
@ -928,7 +933,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
/* clean up carveout allocations */
list_for_each_entry_safe ( entry , tmp , & rproc - > carveouts , node ) {
dma_free_coherent ( dev , entry - > len , entry - > va , entry - > dma ) ;
dma_free_coherent ( dev - > parent , entry - > len , entry - > va , entry - > dma ) ;
list_del ( & entry - > node ) ;
kfree ( entry ) ;
}
@ -953,7 +958,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
static int rproc_fw_sanity_check ( struct rproc * rproc , const struct firmware * fw )
{
const char * name = rproc - > firmware ;
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
struct elf32_hdr * ehdr ;
char class ;
@ -1014,7 +1019,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
*/
static int rproc_fw_boot ( struct rproc * rproc , const struct firmware * fw )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
const char * name = rproc - > firmware ;
struct elf32_hdr * ehdr ;
struct resource_table * table ;
@ -1138,7 +1143,7 @@ int rproc_boot(struct rproc *rproc)
return - EINVAL ;
}
dev = rproc - > dev ;
dev = & rproc - > dev ;
ret = mutex_lock_interruptible ( & rproc - > lock ) ;
if ( ret ) {
@ -1154,7 +1159,7 @@ int rproc_boot(struct rproc *rproc)
}
/* prevent underlying implementation from being removed */
if ( ! try_module_get ( dev - > driver - > owner ) ) {
if ( ! try_module_get ( dev - > parent - > driver - > owner ) ) {
dev_err ( dev , " %s: can't get owner \n " , __func__ ) ;
ret = - EINVAL ;
goto unlock_mutex ;
@ -1181,7 +1186,7 @@ int rproc_boot(struct rproc *rproc)
downref_rproc :
if ( ret ) {
module_put ( dev - > driver - > owner ) ;
module_put ( dev - > parent - > driver - > owner ) ;
atomic_dec ( & rproc - > power ) ;
}
unlock_mutex :
@ -1215,7 +1220,7 @@ EXPORT_SYMBOL(rproc_boot);
*/
void rproc_shutdown ( struct rproc * rproc )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
int ret ;
ret = mutex_lock_interruptible ( & rproc - > lock ) ;
@ -1248,7 +1253,7 @@ void rproc_shutdown(struct rproc *rproc)
out :
mutex_unlock ( & rproc - > lock ) ;
if ( ! ret )
module_put ( dev - > driver - > owner ) ;
module_put ( dev - > parent - > driver - > owner ) ;
}
EXPORT_SYMBOL ( rproc_shutdown ) ;
@ -1271,7 +1276,7 @@ void rproc_release(struct kref *kref)
{
struct rproc * rproc = container_of ( kref , struct rproc , refcount ) ;
dev_info ( rproc - > dev , " removing %s \n " , rproc - > name ) ;
dev_info ( & rproc - > dev , " removing %s \n " , rproc - > name ) ;
rproc_delete_debug_dir ( rproc ) ;
@ -1403,13 +1408,17 @@ EXPORT_SYMBOL(rproc_put);
*/
int rproc_register ( struct rproc * rproc )
{
struct device * dev = rproc - > dev ;
struct device * dev = & rproc - > dev ;
int ret = 0 ;
ret = device_add ( dev ) ;
if ( ret < 0 )
return ret ;
/* expose to rproc_get_by_name users */
klist_add_tail ( & rproc - > node , & rprocs ) ;
dev_info ( rproc - > dev , " %s is available \n " , rproc - > name ) ;
dev_info ( dev , " %s is available \n " , rproc - > name ) ;
dev_info ( dev , " Note: remoteproc is still under development and considered experimental. \n " ) ;
dev_info ( dev , " THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed. \n " ) ;
@ -1441,6 +1450,33 @@ int rproc_register(struct rproc *rproc)
}
EXPORT_SYMBOL ( rproc_register ) ;
/**
* rproc_type_release ( ) - release a remote processor instance
* @ dev : the rproc ' s device
*
* This function should _never_ be called directly .
*
* It will be called by the driver core when no one holds a valid pointer
* to @ dev anymore .
*/
static void rproc_type_release ( struct device * dev )
{
struct rproc * rproc = container_of ( dev , struct rproc , dev ) ;
idr_remove_all ( & rproc - > notifyids ) ;
idr_destroy ( & rproc - > notifyids ) ;
if ( rproc - > index > = 0 )
ida_simple_remove ( & rproc_dev_index , rproc - > index ) ;
kfree ( rproc ) ;
}
static struct device_type rproc_type = {
. name = " remoteproc " ,
. release = rproc_type_release ,
} ;
/**
* rproc_alloc ( ) - allocate a remote processor handle
* @ dev : the underlying device
@ -1479,12 +1515,25 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
return NULL ;
}
rproc - > dev = dev ;
rproc - > name = name ;
rproc - > ops = ops ;
rproc - > firmware = firmware ;
rproc - > priv = & rproc [ 1 ] ;
device_initialize ( & rproc - > dev ) ;
rproc - > dev . parent = dev ;
rproc - > dev . type = & rproc_type ;
/* Assign a unique device index and name */
rproc - > index = ida_simple_get ( & rproc_dev_index , 0 , 0 , GFP_KERNEL ) ;
if ( rproc - > index < 0 ) {
dev_err ( dev , " ida_simple_get failed: %d \n " , rproc - > index ) ;
put_device ( & rproc - > dev ) ;
return NULL ;
}
dev_set_name ( & rproc - > dev , " remoteproc%d " , rproc - > index ) ;
atomic_set ( & rproc - > power , 0 ) ;
kref_init ( & rproc - > refcount ) ;
@ -1516,10 +1565,7 @@ EXPORT_SYMBOL(rproc_alloc);
*/
void rproc_free ( struct rproc * rproc )
{
idr_remove_all ( & rproc - > notifyids ) ;
idr_destroy ( & rproc - > notifyids ) ;
kfree ( rproc ) ;
put_device ( & rproc - > dev ) ;
}
EXPORT_SYMBOL ( rproc_free ) ;
@ -1560,6 +1606,8 @@ int rproc_unregister(struct rproc *rproc)
/* the rproc is downref'ed as soon as it's removed from the klist */
klist_del ( & rproc - > node ) ;
device_del ( & rproc - > dev ) ;
/* the rproc will only be released after its refcount drops to zero */
kref_put ( & rproc - > refcount , rproc_release ) ;
@ -1570,6 +1618,7 @@ EXPORT_SYMBOL(rproc_unregister);
static int __init remoteproc_init ( void )
{
rproc_init_debugfs ( ) ;
return 0 ;
}
module_init ( remoteproc_init ) ;