2011-10-20 17:24:15 +02:00
/*
* Remote Processor Framework
*
* Copyright ( C ) 2011 Texas Instruments , Inc .
* Copyright ( C ) 2011 Google , Inc .
*
* Ohad Ben - Cohen < ohad @ wizery . com >
* Mark Grosen < mgrosen @ ti . com >
* Brian Swetland < swetland @ google . com >
* Fernando Guzman Lugo < fernando . lugo @ ti . com >
* Suman Anna < s - anna @ ti . com >
* Robert Tivy < rtivy @ ti . com >
* Armando Uribe De Leon < x0095078 @ ti . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* 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 .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/kernel.h>
# include <linux/debugfs.h>
# include <linux/remoteproc.h>
# include <linux/device.h>
2012-09-18 12:26:35 +03:00
# include <linux/uaccess.h>
# include "remoteproc_internal.h"
2011-10-20 17:24:15 +02:00
/* remoteproc debugfs parent dir */
static struct dentry * rproc_dbg ;
/*
* Some remote processors may support dumping trace logs into a shared
* memory buffer . We expose this trace buffer using debugfs , so users
* can easily tell what ' s going on remotely .
*
* We will most probably improve the rproc tracing facilities later on ,
* but this kind of lightweight and simple mechanism is always good to have ,
* as it provides very early tracing with little to no dependencies at all .
*/
static ssize_t rproc_trace_read ( struct file * filp , char __user * userbuf ,
2016-08-12 18:42:20 -05:00
size_t count , loff_t * ppos )
2011-10-20 17:24:15 +02:00
{
struct rproc_mem_entry * trace = filp - > private_data ;
int len = strnlen ( trace - > va , trace - > len ) ;
return simple_read_from_buffer ( userbuf , count , ppos , trace - > va , len ) ;
}
static const struct file_operations trace_rproc_ops = {
. read = rproc_trace_read ,
2012-04-05 14:25:11 -07:00
. open = simple_open ,
2011-10-20 17:24:15 +02:00
. llseek = generic_file_llseek ,
} ;
/* expose the name of the remote processor via debugfs */
static ssize_t rproc_name_read ( struct file * filp , char __user * userbuf ,
2016-08-12 18:42:20 -05:00
size_t count , loff_t * ppos )
2011-10-20 17:24:15 +02:00
{
struct rproc * rproc = filp - > private_data ;
/* need room for the name, a newline and a terminating null */
char buf [ 100 ] ;
int i ;
2012-09-25 10:02:51 +03:00
i = scnprintf ( buf , sizeof ( buf ) , " %.98s \n " , rproc - > name ) ;
2011-10-20 17:24:15 +02:00
return simple_read_from_buffer ( userbuf , count , ppos , buf , i ) ;
}
static const struct file_operations rproc_name_ops = {
. read = rproc_name_read ,
2012-04-05 14:25:11 -07:00
. open = simple_open ,
2011-10-20 17:24:15 +02:00
. llseek = generic_file_llseek ,
} ;
2012-09-18 12:26:35 +03:00
/* expose recovery flag via debugfs */
static ssize_t rproc_recovery_read ( struct file * filp , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct rproc * rproc = filp - > private_data ;
char * buf = rproc - > recovery_disabled ? " disabled \n " : " enabled \n " ;
return simple_read_from_buffer ( userbuf , count , ppos , buf , strlen ( buf ) ) ;
}
/*
* By writing to the ' recovery ' debugfs entry , we control the behavior of the
* recovery mechanism dynamically . The default value of this entry is " enabled " .
*
* The ' recovery ' debugfs entry supports these commands :
*
* enabled : When enabled , the remote processor will be automatically
* recovered whenever it crashes . Moreover , if the remote
* processor crashes while recovery is disabled , it will
* be automatically recovered too as soon as recovery is enabled .
*
* disabled : When disabled , a remote processor will remain in a crashed
* state if it crashes . This is useful for debugging purposes ;
* without it , debugging a crash is substantially harder .
*
* recover : This function will trigger an immediate recovery if the
* remote processor is in a crashed state , without changing
* or checking the recovery state ( enabled / disabled ) .
* This is useful during debugging sessions , when one expects
* additional crashes to happen after enabling recovery . In this
* case , enabling recovery will make it hard to debug subsequent
* crashes , so it ' s recommended to keep recovery disabled , and
* instead use the " recover " command as needed .
*/
static ssize_t
rproc_recovery_write ( struct file * filp , const char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct rproc * rproc = filp - > private_data ;
char buf [ 10 ] ;
int ret ;
2015-11-20 18:26:07 +01:00
if ( count < 1 | | count > sizeof ( buf ) )
2016-01-12 12:46:15 +00:00
return - EINVAL ;
2012-09-18 12:26:35 +03:00
ret = copy_from_user ( buf , user_buf , count ) ;
if ( ret )
2012-09-25 10:05:33 +03:00
return - EFAULT ;
2012-09-18 12:26:35 +03:00
/* remove end of line */
if ( buf [ count - 1 ] = = ' \n ' )
buf [ count - 1 ] = ' \0 ' ;
if ( ! strncmp ( buf , " enabled " , count ) ) {
rproc - > recovery_disabled = false ;
/* if rproc has crashed, trigger recovery */
if ( rproc - > state = = RPROC_CRASHED )
rproc_trigger_recovery ( rproc ) ;
} else if ( ! strncmp ( buf , " disabled " , count ) ) {
rproc - > recovery_disabled = true ;
} else if ( ! strncmp ( buf , " recover " , count ) ) {
/* if rproc has crashed, trigger recovery */
if ( rproc - > state = = RPROC_CRASHED )
rproc_trigger_recovery ( rproc ) ;
}
return count ;
}
static const struct file_operations rproc_recovery_ops = {
. read = rproc_recovery_read ,
. write = rproc_recovery_write ,
. open = simple_open ,
. llseek = generic_file_llseek ,
} ;
2017-11-06 18:09:55 +01:00
/* Expose resource table content via debugfs */
static int rproc_rsc_table_show ( struct seq_file * seq , void * p )
{
static const char * const types [ ] = { " carveout " , " devmem " , " trace " , " vdev " } ;
struct rproc * rproc = seq - > private ;
struct resource_table * table = rproc - > table_ptr ;
struct fw_rsc_carveout * c ;
struct fw_rsc_devmem * d ;
struct fw_rsc_trace * t ;
struct fw_rsc_vdev * v ;
int i , j ;
if ( ! table ) {
seq_puts ( seq , " No resource table found \n " ) ;
return 0 ;
}
for ( i = 0 ; i < table - > num ; i + + ) {
int offset = table - > offset [ i ] ;
struct fw_rsc_hdr * hdr = ( void * ) table + offset ;
void * rsc = ( void * ) hdr + sizeof ( * hdr ) ;
switch ( hdr - > type ) {
case RSC_CARVEOUT :
c = rsc ;
seq_printf ( seq , " Entry %d is of type %s \n " , i , types [ hdr - > type ] ) ;
seq_printf ( seq , " Device Address 0x%x \n " , c - > da ) ;
seq_printf ( seq , " Physical Address 0x%x \n " , c - > pa ) ;
seq_printf ( seq , " Length 0x%x Bytes \n " , c - > len ) ;
seq_printf ( seq , " Flags 0x%x \n " , c - > flags ) ;
seq_printf ( seq , " Reserved (should be zero) [%d] \n " , c - > reserved ) ;
seq_printf ( seq , " Name %s \n \n " , c - > name ) ;
break ;
case RSC_DEVMEM :
d = rsc ;
seq_printf ( seq , " Entry %d is of type %s \n " , i , types [ hdr - > type ] ) ;
seq_printf ( seq , " Device Address 0x%x \n " , d - > da ) ;
seq_printf ( seq , " Physical Address 0x%x \n " , d - > pa ) ;
seq_printf ( seq , " Length 0x%x Bytes \n " , d - > len ) ;
seq_printf ( seq , " Flags 0x%x \n " , d - > flags ) ;
seq_printf ( seq , " Reserved (should be zero) [%d] \n " , d - > reserved ) ;
seq_printf ( seq , " Name %s \n \n " , d - > name ) ;
break ;
case RSC_TRACE :
t = rsc ;
seq_printf ( seq , " Entry %d is of type %s \n " , i , types [ hdr - > type ] ) ;
seq_printf ( seq , " Device Address 0x%x \n " , t - > da ) ;
seq_printf ( seq , " Length 0x%x Bytes \n " , t - > len ) ;
seq_printf ( seq , " Reserved (should be zero) [%d] \n " , t - > reserved ) ;
seq_printf ( seq , " Name %s \n \n " , t - > name ) ;
break ;
case RSC_VDEV :
v = rsc ;
seq_printf ( seq , " Entry %d is of type %s \n " , i , types [ hdr - > type ] ) ;
seq_printf ( seq , " ID %d \n " , v - > id ) ;
seq_printf ( seq , " Notify ID %d \n " , v - > notifyid ) ;
seq_printf ( seq , " Device features 0x%x \n " , v - > dfeatures ) ;
seq_printf ( seq , " Guest features 0x%x \n " , v - > gfeatures ) ;
seq_printf ( seq , " Config length 0x%x \n " , v - > config_len ) ;
seq_printf ( seq , " Status 0x%x \n " , v - > status ) ;
seq_printf ( seq , " Number of vrings %d \n " , v - > num_of_vrings ) ;
seq_printf ( seq , " Reserved (should be zero) [%d][%d] \n \n " ,
v - > reserved [ 0 ] , v - > reserved [ 1 ] ) ;
for ( j = 0 ; j < v - > num_of_vrings ; j + + ) {
seq_printf ( seq , " Vring %d \n " , j ) ;
seq_printf ( seq , " Device Address 0x%x \n " , v - > vring [ j ] . da ) ;
seq_printf ( seq , " Alignment %d \n " , v - > vring [ j ] . align ) ;
seq_printf ( seq , " Number of buffers %d \n " , v - > vring [ j ] . num ) ;
seq_printf ( seq , " Notify ID %d \n " , v - > vring [ j ] . notifyid ) ;
seq_printf ( seq , " Physical Address 0x%x \n \n " ,
v - > vring [ j ] . pa ) ;
}
break ;
default :
2018-07-06 14:38:27 +02:00
seq_printf ( seq , " Unknown resource type found: %d [hdr: %pK] \n " ,
2017-11-06 18:09:55 +01:00
hdr - > type , hdr ) ;
break ;
}
}
return 0 ;
}
static int rproc_rsc_table_open ( struct inode * inode , struct file * file )
{
return single_open ( file , rproc_rsc_table_show , inode - > i_private ) ;
}
static const struct file_operations rproc_rsc_table_ops = {
. open = rproc_rsc_table_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2017-11-06 18:09:56 +01:00
/* Expose carveout content via debugfs */
static int rproc_carveouts_show ( struct seq_file * seq , void * p )
{
struct rproc * rproc = seq - > private ;
struct rproc_mem_entry * carveout ;
list_for_each_entry ( carveout , & rproc - > carveouts , node ) {
seq_puts ( seq , " Carveout memory entry: \n " ) ;
2018-07-27 15:14:39 +02:00
seq_printf ( seq , " \t Name: %s \n " , carveout - > name ) ;
2018-07-06 14:38:27 +02:00
seq_printf ( seq , " \t Virtual address: %pK \n " , carveout - > va ) ;
2017-11-06 18:09:56 +01:00
seq_printf ( seq , " \t DMA address: %pad \n " , & carveout - > dma ) ;
seq_printf ( seq , " \t Device address: 0x%x \n " , carveout - > da ) ;
seq_printf ( seq , " \t Length: 0x%x Bytes \n \n " , carveout - > len ) ;
}
return 0 ;
}
static int rproc_carveouts_open ( struct inode * inode , struct file * file )
{
return single_open ( file , rproc_carveouts_show , inode - > i_private ) ;
}
static const struct file_operations rproc_carveouts_ops = {
. open = rproc_carveouts_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2011-10-20 17:24:15 +02:00
void rproc_remove_trace_file ( struct dentry * tfile )
{
debugfs_remove ( tfile ) ;
}
struct dentry * rproc_create_trace_file ( const char * name , struct rproc * rproc ,
2016-08-12 18:42:20 -05:00
struct rproc_mem_entry * trace )
2011-10-20 17:24:15 +02:00
{
struct dentry * tfile ;
2016-08-12 18:42:20 -05:00
tfile = debugfs_create_file ( name , 0400 , rproc - > dbg_dir , trace ,
& trace_rproc_ops ) ;
2011-10-20 17:24:15 +02:00
if ( ! tfile ) {
remoteproc: maintain a generic child device for each rproc
For each registered rproc, maintain a generic remoteproc device whose
parent is the low level platform-specific device (commonly a pdev, but
it may certainly be any other type of device too).
With this in hand, the resulting device hierarchy might then look like:
omap-rproc.0
|
- remoteproc0 <---- new !
|
- virtio0
|
- virtio1
|
- rpmsg0
|
- rpmsg1
|
- rpmsg2
Where:
- omap-rproc.0 is the low level device that's bound to the
driver which invokes rproc_register()
- remoteproc0 is the result of this patch, and will be added by the
remoteproc framework when rproc_register() is invoked
- virtio0 and virtio1 are vdevs that are registered by remoteproc
when it realizes that they are supported by the firmware
of the physical remote processor represented by omap-rproc.0
- rpmsg0, rpmsg1 and rpmsg2 are rpmsg devices that represent rpmsg
channels, and are registerd by the rpmsg bus when it gets notified
about their existence
Technically, this patch:
- changes 'struct rproc' to contain this generic remoteproc.x device
- creates a new "remoteproc" type, to which this new generic remoteproc.x
device belong to.
- adds a super simple enumeration method for the indices of the
remoteproc.x devices
- updates all dev_* messaging to use the generic remoteproc.x device
instead of the low level platform-specific device
- updates all dma_* allocations to use the parent of remoteproc.x (where
the platform-specific memory pools, most commonly CMA, are to be found)
Adding this generic device has several merits:
- we can now add remoteproc runtime PM support simply by hooking onto the
new "remoteproc" type
- all remoteproc log messages will now carry a common name prefix
instead of having a platform-specific one
- having a device as part of the rproc struct makes it possible to simplify
refcounting (see subsequent patch)
Thanks to Stephen Boyd <sboyd@codeaurora.org> for suggesting and
discussing these ideas in one of the remoteproc review threads and
to Fernando Guzman Lugo <fernando.lugo@ti.com> for trying them out
with the (upcoming) runtime PM support for remoteproc.
Cc: Fernando Guzman Lugo <fernando.lugo@ti.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
2012-05-30 22:01:25 +03:00
dev_err ( & rproc - > dev , " failed to create debugfs trace entry \n " ) ;
2011-10-20 17:24:15 +02:00
return NULL ;
}
return tfile ;
}
void rproc_delete_debug_dir ( struct rproc * rproc )
{
if ( ! rproc - > dbg_dir )
return ;
debugfs_remove_recursive ( rproc - > dbg_dir ) ;
}
void rproc_create_debug_dir ( struct rproc * rproc )
{
remoteproc: maintain a generic child device for each rproc
For each registered rproc, maintain a generic remoteproc device whose
parent is the low level platform-specific device (commonly a pdev, but
it may certainly be any other type of device too).
With this in hand, the resulting device hierarchy might then look like:
omap-rproc.0
|
- remoteproc0 <---- new !
|
- virtio0
|
- virtio1
|
- rpmsg0
|
- rpmsg1
|
- rpmsg2
Where:
- omap-rproc.0 is the low level device that's bound to the
driver which invokes rproc_register()
- remoteproc0 is the result of this patch, and will be added by the
remoteproc framework when rproc_register() is invoked
- virtio0 and virtio1 are vdevs that are registered by remoteproc
when it realizes that they are supported by the firmware
of the physical remote processor represented by omap-rproc.0
- rpmsg0, rpmsg1 and rpmsg2 are rpmsg devices that represent rpmsg
channels, and are registerd by the rpmsg bus when it gets notified
about their existence
Technically, this patch:
- changes 'struct rproc' to contain this generic remoteproc.x device
- creates a new "remoteproc" type, to which this new generic remoteproc.x
device belong to.
- adds a super simple enumeration method for the indices of the
remoteproc.x devices
- updates all dev_* messaging to use the generic remoteproc.x device
instead of the low level platform-specific device
- updates all dma_* allocations to use the parent of remoteproc.x (where
the platform-specific memory pools, most commonly CMA, are to be found)
Adding this generic device has several merits:
- we can now add remoteproc runtime PM support simply by hooking onto the
new "remoteproc" type
- all remoteproc log messages will now carry a common name prefix
instead of having a platform-specific one
- having a device as part of the rproc struct makes it possible to simplify
refcounting (see subsequent patch)
Thanks to Stephen Boyd <sboyd@codeaurora.org> for suggesting and
discussing these ideas in one of the remoteproc review threads and
to Fernando Guzman Lugo <fernando.lugo@ti.com> for trying them out
with the (upcoming) runtime PM support for remoteproc.
Cc: Fernando Guzman Lugo <fernando.lugo@ti.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
2012-05-30 22:01:25 +03:00
struct device * dev = & rproc - > dev ;
2011-10-20 17:24:15 +02:00
if ( ! rproc_dbg )
return ;
rproc - > dbg_dir = debugfs_create_dir ( dev_name ( dev ) , rproc_dbg ) ;
if ( ! rproc - > dbg_dir )
return ;
debugfs_create_file ( " name " , 0400 , rproc - > dbg_dir ,
2016-08-12 18:42:20 -05:00
rproc , & rproc_name_ops ) ;
2012-09-18 12:26:35 +03:00
debugfs_create_file ( " recovery " , 0400 , rproc - > dbg_dir ,
2016-08-12 18:42:20 -05:00
rproc , & rproc_recovery_ops ) ;
2017-11-06 18:09:55 +01:00
debugfs_create_file ( " resource_table " , 0400 , rproc - > dbg_dir ,
rproc , & rproc_rsc_table_ops ) ;
2017-11-06 18:09:56 +01:00
debugfs_create_file ( " carveout_memories " , 0400 , rproc - > dbg_dir ,
rproc , & rproc_carveouts_ops ) ;
2011-10-20 17:24:15 +02:00
}
void __init rproc_init_debugfs ( void )
{
if ( debugfs_initialized ( ) ) {
rproc_dbg = debugfs_create_dir ( KBUILD_MODNAME , NULL ) ;
if ( ! rproc_dbg )
pr_err ( " can't create debugfs dir \n " ) ;
}
}
void __exit rproc_exit_debugfs ( void )
{
2013-06-30 11:33:05 +03:00
debugfs_remove ( rproc_dbg ) ;
2011-10-20 17:24:15 +02:00
}