mfd: display-serdes: Add serdes debug node

Change-Id: Ieeb8590e184cfa45d32ff72a69b0c2ac8e43a3bc
Signed-off-by: Zitong Cai <zitong.cai@rock-chips.com>
This commit is contained in:
Zitong Cai
2025-05-05 17:07:17 +08:00
committed by Tao Huang
parent ba953750af
commit d91c7665f9
3 changed files with 229 additions and 29 deletions

View File

@@ -78,36 +78,42 @@
#include "../../../../drivers/extcon/extcon.h"
#include "../../../../drivers/base/regmap/internal.h"
/*
* if enable all the debug information,
* there will be much log.
*
* so suggest set CONFIG_LOG_BUF_SHIFT to 18
*/
//#define SERDES_DEBUG_MFD
//#define SERDES_DEBUG_I2C
//#define SERDES_DEBUG_CHIP
/**
* Enabling verbose debug messages is done through the serdes_log_level parameter, each
* category being enabled by a bit:
*
* - serdes_log_level=0x1 will enable MFD messages
* - serdes_log_level=0x2 will enable I2C messages
* - serdes_log_level=0x4 will enable CHIP messages
* - serdes_log_level=0x7 will enable all messages
*
* An interesting feature is that it's possible to enable verbose logging at
* run-time by echoing the debug value in its sysfs node::
*
* # echo 0x7 > /sys/kernel/debug/log_level
**/
#ifdef SERDES_DEBUG_MFD
#define SERDES_DBG_MFD(x...) pr_info(x)
#else
#define SERDES_DBG_MFD(x...) no_printk(x)
#endif
enum serdes_log_category {
SERDES_MFD,
SERDES_I2C,
SERDES_CHIP,
};
#ifdef SERDES_DEBUG_I2C
#define SERDES_DBG_I2C(x...) pr_info(x)
#else
#define SERDES_DBG_I2C(x...) no_printk(x)
#endif
#define SERDES_DBG_MFD(fmt, ...) serdes_dev_dbg(SERDES_MFD, fmt, ##__VA_ARGS__)
#define SERDES_DBG_I2C(fmt, ...) serdes_dev_dbg(SERDES_I2C, fmt, ##__VA_ARGS__)
#define SERDES_DBG_CHIP(fmt, ...) serdes_dev_dbg(SERDES_CHIP, fmt, ##__VA_ARGS__)
#ifdef SERDES_DEBUG_CHIP
#define SERDES_DBG_CHIP(x...) pr_info(x)
#else
#define SERDES_DBG_CHIP(x...) no_printk(x)
#endif
enum serdes_debug_mode {
SERDES_OPEN_I2C_WRITE,
SERDES_CLOSE_I2C_WRITE,
SERDES_SET_SEQUENCE,
SERDES_SET_PINCTRL_SLEEP,
SERDES_SET_PINCTRL_DEFAULT,
};
#define MFD_SERDES_DISPLAY_VERSION "serdes-mfd-displaly-v11-241025"
#define MAX_NUM_SERDES_SPLIT 8
struct serdes;
enum ser_link_mode {
SER_DUAL_LINK,
@@ -387,8 +393,12 @@ struct serdes {
struct workqueue_struct *mfd_wq;
struct delayed_work mfd_delay_work;
bool route_enable;
bool use_delay_work;
char dir_name[25];
struct dentry *debugfs_dentry;
enum serdes_debug_mode debug;
struct kthread_worker *kworker;
struct kthread_delayed_work reg_check_work;
@@ -420,8 +430,6 @@ struct serdes {
/* Device I/O API */
int serdes_reg_read(struct serdes *serdes, unsigned int reg, unsigned int *val);
int serdes_reg_write(struct serdes *serdes, unsigned int reg, unsigned int val);
void serdes_reg_lock(struct serdes *serdes);
int serdes_reg_unlock(struct serdes *serdes);
int serdes_set_bits(struct serdes *serdes, unsigned int reg,
unsigned int mask, unsigned int val);
int serdes_bulk_read(struct serdes *serdes, unsigned int reg,
@@ -441,7 +449,12 @@ void serdes_device_poweroff(struct serdes *serdes);
int serdes_device_shutdown(struct serdes *serdes);
int serdes_irq_init(struct serdes *serdes);
void serdes_irq_exit(struct serdes *serdes);
void serdes_auxadc_init(struct serdes *serdes);
void serdes_dev_dbg(enum serdes_log_category category, const char *format, ...);
void serdes_debugfs_init(void);
void serdes_debugfs_exit(void);
void serdes_create_debugfs(struct serdes *serdes);
void serdes_destroy_debugfs(struct serdes *serdes);
extern struct serdes_chip_data serdes_bu18tl82_data;
extern struct serdes_chip_data serdes_bu18rl82_data;

View File

@@ -9,6 +9,9 @@
#include "core.h"
static unsigned long serdes_log_level;
static struct dentry *serdes_debugfs_root;
static const struct mfd_cell serdes_bu18tl82_devs[] = {
{
.name = "serdes-pinctrl",
@@ -175,6 +178,9 @@ int serdes_bulk_write(struct serdes *serdes, unsigned int reg,
u16 *buf = src;
int i, ret;
if (serdes->debug == SERDES_CLOSE_I2C_WRITE)
return 0;
WARN_ON(count <= 0);
mutex_lock(&serdes->io_lock);
@@ -205,6 +211,9 @@ int serdes_multi_reg_write(struct serdes *serdes, const struct reg_sequence *reg
{
int i, ret;
if (serdes->debug == SERDES_CLOSE_I2C_WRITE)
return 0;
SERDES_DBG_I2C("%s %s %s num=%d\n", __func__, dev_name(serdes->dev),
serdes->chip_data->name, num_regs);
ret = regmap_multi_reg_write(serdes->regmap, regs, num_regs);
@@ -229,6 +238,9 @@ int serdes_reg_write(struct serdes *serdes, unsigned int reg,
{
int ret;
if (serdes->debug == SERDES_CLOSE_I2C_WRITE)
return 0;
ret = regmap_write(serdes->regmap, reg, val);
SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x ret=%d\n", __func__, dev_name(serdes->dev),
serdes->chip_data->name, reg, val, ret);
@@ -252,6 +264,9 @@ int serdes_set_bits(struct serdes *serdes, unsigned int reg,
{
int ret;
if (serdes->debug == SERDES_CLOSE_I2C_WRITE)
return 0;
SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x) mask=%04x\n", __func__,
dev_name(serdes->dev), serdes->chip_data->name, reg, val, mask);
ret = regmap_update_bits(serdes->regmap, reg, mask, val);
@@ -390,6 +405,162 @@ int serdes_device_init(struct serdes *serdes)
}
EXPORT_SYMBOL_GPL(serdes_device_init);
static int log_level_show(struct seq_file *m, void *data)
{
seq_printf(m, "%lu\n", serdes_log_level);
return 0;
}
static int log_level_open(struct inode *inode, struct file *file)
{
return single_open(file, log_level_show, NULL);
}
static ssize_t log_level_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
char buf[12];
unsigned long value;
if (len > sizeof(buf) - 1)
return -EINVAL;
if (copy_from_user(buf, ubuf, len))
return -EFAULT;
buf[len] = '\0';
if (kstrtoul(buf, 0, &value))
return -EINVAL;
serdes_log_level = value;
return len;
}
static int debug_show(struct seq_file *m, void *data)
{
struct serdes *serdes = m->private;
seq_printf(m, "%d\n", serdes->debug);
return 0;
}
static int debug_open(struct inode *inode, struct file *file)
{
struct serdes *serdes = inode->i_private;
return single_open(file, debug_show, serdes);
}
static ssize_t debug_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct serdes *serdes = m->private;
char buf[12];
if (!serdes)
return -EINVAL;
if (len > sizeof(buf) - 1)
return -EINVAL;
if (copy_from_user(buf, ubuf, len))
return -EFAULT;
buf[len] = '\0';
if (sysfs_streq(buf, "on"))
serdes->debug = SERDES_OPEN_I2C_WRITE;
else if (sysfs_streq(buf, "off"))
serdes->debug = SERDES_CLOSE_I2C_WRITE;
else if (sysfs_streq(buf, "default")) {
serdes->debug = SERDES_SET_PINCTRL_DEFAULT;
serdes_set_pinctrl_default(serdes);
} else if (sysfs_streq(buf, "sleep")) {
serdes->debug = SERDES_SET_PINCTRL_SLEEP;
serdes_set_pinctrl_sleep(serdes);
} else if (sysfs_streq(buf, "seq")) {
serdes->debug = SERDES_SET_SEQUENCE;
serdes_i2c_set_sequence(serdes);
} else
return -EINVAL;
return len;
}
static const struct file_operations log_level_fops = {
.owner = THIS_MODULE,
.open = log_level_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = log_level_write
};
static const struct file_operations debug_fops = {
.owner = THIS_MODULE,
.open = debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = debug_write
};
void serdes_create_debugfs(struct serdes *serdes)
{
snprintf(serdes->dir_name, sizeof(serdes->dir_name), "%s-%s",
dev_name(serdes->dev), serdes->chip_data->name);
serdes->debugfs_dentry = debugfs_create_dir(serdes->dir_name, serdes_debugfs_root);
debugfs_create_file("debug", 0664, serdes->debugfs_dentry, serdes,
&debug_fops);
}
EXPORT_SYMBOL(serdes_create_debugfs);
void serdes_destroy_debugfs(struct serdes *serdes)
{
debugfs_remove_recursive(serdes->debugfs_dentry);
}
EXPORT_SYMBOL(serdes_destroy_debugfs);
void serdes_debugfs_init(void)
{
serdes_debugfs_root = debugfs_create_dir("serdes", NULL);
debugfs_create_file("log_level", 0664, serdes_debugfs_root, NULL,
&log_level_fops);
}
EXPORT_SYMBOL(serdes_debugfs_init);
void serdes_debugfs_exit(void)
{
debugfs_remove_recursive(serdes_debugfs_root);
}
EXPORT_SYMBOL(serdes_debugfs_exit);
void serdes_dev_dbg(enum serdes_log_category category, const char *format, ...)
{
struct va_format vaf;
va_list args;
if (!unlikely(serdes_log_level & BIT(category)))
return;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
pr_info("%pV", &vaf);
va_end(args);
}
EXPORT_SYMBOL(serdes_dev_dbg);
int serdes_set_pinctrl_default(struct serdes *serdes)
{
int ret = 0;

View File

@@ -446,6 +446,8 @@ static int serdes_i2c_probe(struct i2c_client *client,
SERDES_DBG_MFD("%s: use_reg_check_work=%d\n", __func__, serdes->use_reg_check_work);
}
serdes_create_debugfs(serdes);
dev_info(dev, "serdes %s serdes_i2c_probe successful version %s\n",
serdes->chip_data->name, MFD_SERDES_DISPLAY_VERSION);
@@ -472,6 +474,8 @@ static void serdes_i2c_remove(struct i2c_client *client)
cancel_delayed_work_sync(&serdes->mfd_delay_work);
destroy_workqueue(serdes->mfd_wq);
}
serdes_destroy_debugfs(serdes);
}
static int serdes_i2c_prepare(struct device *dev)
@@ -584,12 +588,24 @@ static int __init serdes_i2c_init(void)
int ret;
ret = i2c_add_driver(&serdes_i2c_driver);
if (ret != 0)
if (ret != 0) {
pr_err("Failed to register serdes I2C driver: %d\n", ret);
return ret;
}
return ret;
serdes_debugfs_init();
return 0;
}
static void __exit serdes_i2c_exit(void)
{
i2c_del_driver(&serdes_i2c_driver);
serdes_debugfs_exit();
}
subsys_initcall(serdes_i2c_init);
module_exit(serdes_i2c_exit);
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
MODULE_DESCRIPTION("display i2c interface for different serdes");