2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-10-20 19:24:15 +04: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 >
*/
# 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 13:26:35 +04:00
# include <linux/uaccess.h>
# include "remoteproc_internal.h"
2011-10-20 19:24:15 +04: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-13 02:42:20 +03:00
size_t count , loff_t * ppos )
2011-10-20 19:24:15 +04:00
{
2019-01-10 16:49:10 +03:00
struct rproc_debug_trace * data = filp - > private_data ;
struct rproc_mem_entry * trace = & data - > trace_mem ;
void * va ;
char buf [ 100 ] ;
int len ;
va = rproc_da_to_va ( data - > rproc , trace - > da , trace - > len ) ;
if ( ! va ) {
len = scnprintf ( buf , sizeof ( buf ) , " Trace %s not available \n " ,
trace - > name ) ;
va = buf ;
} else {
len = strnlen ( va , trace - > len ) ;
}
2011-10-20 19:24:15 +04:00
2019-01-10 16:49:10 +03:00
return simple_read_from_buffer ( userbuf , count , ppos , va , len ) ;
2011-10-20 19:24:15 +04:00
}
static const struct file_operations trace_rproc_ops = {
. read = rproc_trace_read ,
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2011-10-20 19:24:15 +04: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-13 02:42:20 +03:00
size_t count , loff_t * ppos )
2011-10-20 19:24:15 +04: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 11:02:51 +04:00
i = scnprintf ( buf , sizeof ( buf ) , " %.98s \n " , rproc - > name ) ;
2011-10-20 19:24:15 +04: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-06 01:25:11 +04:00
. open = simple_open ,
2011-10-20 19:24:15 +04:00
. llseek = generic_file_llseek ,
} ;
2012-09-18 13:26:35 +04: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 20:26:07 +03:00
if ( count < 1 | | count > sizeof ( buf ) )
2016-01-12 15:46:15 +03:00
return - EINVAL ;
2012-09-18 13:26:35 +04:00
ret = copy_from_user ( buf , user_buf , count ) ;
if ( ret )
2012-09-25 11:05:33 +04:00
return - EFAULT ;
2012-09-18 13:26:35 +04:00
/* remove end of line */
if ( buf [ count - 1 ] = = ' \n ' )
buf [ count - 1 ] = ' \0 ' ;
if ( ! strncmp ( buf , " enabled " , count ) ) {
2020-02-28 21:33:57 +03:00
/* change the flag and begin the recovery process if needed */
2012-09-18 13:26:35 +04:00
rproc - > recovery_disabled = false ;
2020-02-28 21:33:57 +03:00
rproc_trigger_recovery ( rproc ) ;
2012-09-18 13:26:35 +04:00
} else if ( ! strncmp ( buf , " disabled " , count ) ) {
rproc - > recovery_disabled = true ;
} else if ( ! strncmp ( buf , " recover " , count ) ) {
2020-02-28 21:33:57 +03:00
/* begin the recovery process without changing the flag */
rproc_trigger_recovery ( rproc ) ;
2020-02-28 21:33:59 +03:00
} else {
return - EINVAL ;
2012-09-18 13:26:35 +04:00
}
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 ,
} ;
2018-11-07 18:26:01 +03:00
/* expose the crash trigger via debugfs */
static ssize_t
rproc_crash_write ( struct file * filp , const char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct rproc * rproc = filp - > private_data ;
unsigned int type ;
int ret ;
ret = kstrtouint_from_user ( user_buf , count , 0 , & type ) ;
if ( ret < 0 )
return ret ;
rproc_report_crash ( rproc , type ) ;
return count ;
}
static const struct file_operations rproc_crash_ops = {
. write = rproc_crash_write ,
. open = simple_open ,
. llseek = generic_file_llseek ,
} ;
2017-11-06 20:09:55 +03: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 15:38:27 +03:00
seq_printf ( seq , " Unknown resource type found: %d [hdr: %pK] \n " ,
2017-11-06 20:09:55 +03:00
hdr - > type , hdr ) ;
break ;
}
}
return 0 ;
}
2018-12-01 18:58:38 +03:00
DEFINE_SHOW_ATTRIBUTE ( rproc_rsc_table ) ;
2017-11-06 20:09:55 +03:00
2017-11-06 20:09:56 +03: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 16:14:39 +03:00
seq_printf ( seq , " \t Name: %s \n " , carveout - > name ) ;
2018-07-06 15:38:27 +03:00
seq_printf ( seq , " \t Virtual address: %pK \n " , carveout - > va ) ;
2017-11-06 20:09:56 +03:00
seq_printf ( seq , " \t DMA address: %pad \n " , & carveout - > dma ) ;
seq_printf ( seq , " \t Device address: 0x%x \n " , carveout - > da ) ;
2020-03-02 12:38:56 +03:00
seq_printf ( seq , " \t Length: 0x%zx Bytes \n \n " , carveout - > len ) ;
2017-11-06 20:09:56 +03:00
}
return 0 ;
}
2018-12-01 18:58:38 +03:00
DEFINE_SHOW_ATTRIBUTE ( rproc_carveouts ) ;
2017-11-06 20:09:56 +03:00
2011-10-20 19:24:15 +04:00
void rproc_remove_trace_file ( struct dentry * tfile )
{
debugfs_remove ( tfile ) ;
}
struct dentry * rproc_create_trace_file ( const char * name , struct rproc * rproc ,
2019-01-10 16:49:10 +03:00
struct rproc_debug_trace * trace )
2011-10-20 19:24:15 +04:00
{
struct dentry * tfile ;
2016-08-13 02:42:20 +03:00
tfile = debugfs_create_file ( name , 0400 , rproc - > dbg_dir , trace ,
& trace_rproc_ops ) ;
2011-10-20 19:24:15 +04: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 23:01:25 +04:00
dev_err ( & rproc - > dev , " failed to create debugfs trace entry \n " ) ;
2011-10-20 19:24:15 +04:00
return NULL ;
}
return tfile ;
}
void rproc_delete_debug_dir ( struct rproc * rproc )
{
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 23:01:25 +04:00
struct device * dev = & rproc - > dev ;
2011-10-20 19:24:15 +04: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-13 02:42:20 +03:00
rproc , & rproc_name_ops ) ;
2020-02-28 21:33:57 +03:00
debugfs_create_file ( " recovery " , 0600 , rproc - > dbg_dir ,
2016-08-13 02:42:20 +03:00
rproc , & rproc_recovery_ops ) ;
2018-11-07 18:26:01 +03:00
debugfs_create_file ( " crash " , 0200 , rproc - > dbg_dir ,
rproc , & rproc_crash_ops ) ;
2017-11-06 20:09:55 +03:00
debugfs_create_file ( " resource_table " , 0400 , rproc - > dbg_dir ,
2018-12-01 18:58:38 +03:00
rproc , & rproc_rsc_table_fops ) ;
2017-11-06 20:09:56 +03:00
debugfs_create_file ( " carveout_memories " , 0400 , rproc - > dbg_dir ,
2018-12-01 18:58:38 +03:00
rproc , & rproc_carveouts_fops ) ;
2011-10-20 19:24:15 +04: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 12:33:05 +04:00
debugfs_remove ( rproc_dbg ) ;
2011-10-20 19:24:15 +04:00
}