mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user