2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-22 18:34:04 +04:00
/*
* Copyright ( C ) 2010 Google , Inc .
* Author : Erik Gilling < konkers @ android . com >
*
* Copyright ( C ) 2011 - 2013 NVIDIA Corporation
*/
# include <linux/debugfs.h>
2021-12-01 02:23:15 +03:00
# include <linux/pm_runtime.h>
2013-03-22 18:34:04 +04:00
# include <linux/seq_file.h>
# include <linux/uaccess.h>
# include <linux/io.h>
# include "dev.h"
# include "debug.h"
# include "channel.h"
2020-06-29 06:18:41 +03:00
static DEFINE_MUTEX ( debug_lock ) ;
2013-03-22 18:34:04 +04:00
unsigned int host1x_debug_trace_cmdbuf ;
static pid_t host1x_debug_force_timeout_pid ;
static u32 host1x_debug_force_timeout_val ;
static u32 host1x_debug_force_timeout_channel ;
void host1x_debug_output ( struct output * o , const char * fmt , . . . )
{
va_list args ;
int len ;
va_start ( args , fmt ) ;
len = vsnprintf ( o - > buf , sizeof ( o - > buf ) , fmt , args ) ;
va_end ( args ) ;
2016-06-23 12:35:50 +03:00
2017-09-28 15:50:41 +03:00
o - > fn ( o - > ctx , o - > buf , len , false ) ;
}
void host1x_debug_cont ( struct output * o , const char * fmt , . . . )
{
va_list args ;
int len ;
va_start ( args , fmt ) ;
len = vsnprintf ( o - > buf , sizeof ( o - > buf ) , fmt , args ) ;
va_end ( args ) ;
o - > fn ( o - > ctx , o - > buf , len , true ) ;
2013-03-22 18:34:04 +04:00
}
2017-06-15 02:18:42 +03:00
static int show_channel ( struct host1x_channel * ch , void * data , bool show_fifo )
2013-03-22 18:34:04 +04:00
{
struct host1x * m = dev_get_drvdata ( ch - > dev - > parent ) ;
struct output * o = data ;
2021-12-01 02:23:15 +03:00
int err ;
err = pm_runtime_resume_and_get ( m - > dev ) ;
if ( err < 0 )
return err ;
2013-03-22 18:34:04 +04:00
2017-06-15 02:18:42 +03:00
mutex_lock ( & ch - > cdma . lock ) ;
2020-06-29 06:18:41 +03:00
mutex_lock ( & debug_lock ) ;
2016-06-23 12:35:50 +03:00
2017-06-15 02:18:42 +03:00
if ( show_fifo )
host1x_hw_show_channel_fifo ( m , ch , o ) ;
2016-06-23 12:35:50 +03:00
2017-06-15 02:18:42 +03:00
host1x_hw_show_channel_cdma ( m , ch , o ) ;
2016-06-23 12:35:50 +03:00
2020-06-29 06:18:41 +03:00
mutex_unlock ( & debug_lock ) ;
2017-06-15 02:18:42 +03:00
mutex_unlock ( & ch - > cdma . lock ) ;
2013-03-22 18:34:04 +04:00
2021-12-01 02:23:15 +03:00
pm_runtime_put ( m - > dev ) ;
2013-03-22 18:34:04 +04:00
return 0 ;
}
2022-01-14 17:04:53 +03:00
static void show_syncpts ( struct host1x * m , struct output * o , bool show_all )
2013-03-22 18:34:04 +04:00
{
2023-01-19 16:09:20 +03:00
unsigned long irqflags ;
2021-03-29 16:38:29 +03:00
struct list_head * pos ;
2016-06-22 17:44:07 +03:00
unsigned int i ;
2021-12-01 02:23:15 +03:00
int err ;
2016-06-23 12:33:31 +03:00
2013-03-22 18:34:04 +04:00
host1x_debug_output ( o , " ---- syncpts ---- \n " ) ;
2016-06-23 12:35:50 +03:00
2021-12-01 02:23:15 +03:00
err = pm_runtime_resume_and_get ( m - > dev ) ;
if ( err < 0 )
return ;
2013-03-22 18:34:04 +04:00
for ( i = 0 ; i < host1x_syncpt_nb_pts ( m ) ; i + + ) {
u32 max = host1x_syncpt_read_max ( m - > syncpt + i ) ;
u32 min = host1x_syncpt_load ( m - > syncpt + i ) ;
2021-03-29 16:38:29 +03:00
unsigned int waiters = 0 ;
2016-06-23 12:33:31 +03:00
2023-01-19 16:09:20 +03:00
spin_lock_irqsave ( & m - > syncpt [ i ] . fences . lock , irqflags ) ;
list_for_each ( pos , & m - > syncpt [ i ] . fences . list )
2021-03-29 16:38:29 +03:00
waiters + + ;
2023-01-19 16:09:20 +03:00
spin_unlock_irqrestore ( & m - > syncpt [ i ] . fences . lock , irqflags ) ;
2021-03-29 16:38:29 +03:00
2022-01-14 17:04:53 +03:00
if ( ! kref_read ( & m - > syncpt [ i ] . ref ) )
continue ;
if ( ! show_all & & ! min & & ! max & & ! waiters )
2013-03-22 18:34:04 +04:00
continue ;
2016-06-22 17:44:07 +03:00
2021-03-29 16:38:29 +03:00
host1x_debug_output ( o ,
" id %u (%s) min %d max %d (%d waiters) \n " ,
i , m - > syncpt [ i ] . name , min , max , waiters ) ;
2013-03-22 18:34:04 +04:00
}
for ( i = 0 ; i < host1x_syncpt_nb_bases ( m ) ; i + + ) {
u32 base_val ;
2016-06-23 12:33:31 +03:00
2013-03-22 18:34:04 +04:00
base_val = host1x_syncpt_load_wait_base ( m - > syncpt + i ) ;
if ( base_val )
2016-06-22 17:44:07 +03:00
host1x_debug_output ( o , " waitbase id %u val %d \n " , i ,
2013-03-22 18:34:04 +04:00
base_val ) ;
}
2021-12-01 02:23:15 +03:00
pm_runtime_put ( m - > dev ) ;
2013-03-22 18:34:04 +04:00
host1x_debug_output ( o , " \n " ) ;
}
2017-06-15 02:18:42 +03:00
static void show_all ( struct host1x * m , struct output * o , bool show_fifo )
2013-03-22 18:34:04 +04:00
{
2018-03-23 15:31:24 +03:00
unsigned int i ;
2013-03-22 18:34:04 +04:00
host1x_hw_show_mlocks ( m , o ) ;
2022-01-14 17:04:53 +03:00
show_syncpts ( m , o , true ) ;
2013-03-22 18:34:04 +04:00
host1x_debug_output ( o , " ---- channels ---- \n " ) ;
2017-06-15 02:18:42 +03:00
for ( i = 0 ; i < m - > info - > nb_channels ; + + i ) {
struct host1x_channel * ch = host1x_channel_get_index ( m , i ) ;
2013-03-22 18:34:04 +04:00
2017-06-15 02:18:42 +03:00
if ( ch ) {
show_channel ( ch , o , show_fifo ) ;
host1x_channel_put ( ch ) ;
}
}
2013-03-22 18:34:04 +04:00
}
2022-09-22 17:23:16 +03:00
static int host1x_debug_all_show ( struct seq_file * s , void * unused )
2013-03-22 18:34:04 +04:00
{
struct output o = {
. fn = write_to_seqfile ,
. ctx = s
} ;
2016-06-23 12:35:50 +03:00
2017-06-15 02:18:42 +03:00
show_all ( s - > private , & o , true ) ;
2016-06-23 12:35:50 +03:00
2013-03-22 18:34:04 +04:00
return 0 ;
}
2022-09-22 17:23:16 +03:00
DEFINE_SHOW_ATTRIBUTE ( host1x_debug_all ) ;
2013-03-22 18:34:04 +04:00
static int host1x_debug_show ( struct seq_file * s , void * unused )
{
struct output o = {
. fn = write_to_seqfile ,
. ctx = s
} ;
2016-06-23 12:35:50 +03:00
2017-06-15 02:18:42 +03:00
show_all ( s - > private , & o , false ) ;
2016-06-23 12:35:50 +03:00
2013-03-22 18:34:04 +04:00
return 0 ;
}
2022-09-22 17:23:16 +03:00
DEFINE_SHOW_ATTRIBUTE ( host1x_debug ) ;
2013-03-22 18:34:04 +04:00
2013-12-03 14:44:48 +04:00
static void host1x_debugfs_init ( struct host1x * host1x )
2013-03-22 18:34:04 +04:00
{
struct dentry * de = debugfs_create_dir ( " tegra-host1x " , NULL ) ;
/* Store the created entry */
host1x - > debugfs = de ;
debugfs_create_file ( " status " , S_IRUGO , de , host1x , & host1x_debug_fops ) ;
debugfs_create_file ( " status_all " , S_IRUGO , de , host1x ,
& host1x_debug_all_fops ) ;
debugfs_create_u32 ( " trace_cmdbuf " , S_IRUGO | S_IWUSR , de ,
& host1x_debug_trace_cmdbuf ) ;
host1x_hw_debug_init ( host1x , de ) ;
debugfs_create_u32 ( " force_timeout_pid " , S_IRUGO | S_IWUSR , de ,
& host1x_debug_force_timeout_pid ) ;
debugfs_create_u32 ( " force_timeout_val " , S_IRUGO | S_IWUSR , de ,
& host1x_debug_force_timeout_val ) ;
debugfs_create_u32 ( " force_timeout_channel " , S_IRUGO | S_IWUSR , de ,
& host1x_debug_force_timeout_channel ) ;
}
2013-12-03 14:44:48 +04:00
static void host1x_debugfs_exit ( struct host1x * host1x )
2013-03-22 18:34:04 +04:00
{
debugfs_remove_recursive ( host1x - > debugfs ) ;
}
2013-12-03 14:44:48 +04:00
2013-03-22 18:34:04 +04:00
void host1x_debug_init ( struct host1x * host1x )
{
2013-12-03 14:44:48 +04:00
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) )
host1x_debugfs_init ( host1x ) ;
2013-03-22 18:34:04 +04:00
}
2013-12-03 14:44:48 +04:00
2013-03-22 18:34:04 +04:00
void host1x_debug_deinit ( struct host1x * host1x )
{
2013-12-03 14:44:48 +04:00
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) )
host1x_debugfs_exit ( host1x ) ;
2013-03-22 18:34:04 +04:00
}
void host1x_debug_dump ( struct host1x * host1x )
{
struct output o = {
. fn = write_to_printk
} ;
2016-06-23 12:35:50 +03:00
2017-06-15 02:18:42 +03:00
show_all ( host1x , & o , true ) ;
2013-03-22 18:34:04 +04:00
}
void host1x_debug_dump_syncpts ( struct host1x * host1x )
{
struct output o = {
. fn = write_to_printk
} ;
2016-06-23 12:35:50 +03:00
2022-01-14 17:04:53 +03:00
show_syncpts ( host1x , & o , false ) ;
2013-03-22 18:34:04 +04:00
}