diff --git a/drivers/mfd/display-serdes/core.h b/drivers/mfd/display-serdes/core.h index e9b1169ac7dd..f51c7d8e609e 100644 --- a/drivers/mfd/display-serdes/core.h +++ b/drivers/mfd/display-serdes/core.h @@ -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; diff --git a/drivers/mfd/display-serdes/serdes-core.c b/drivers/mfd/display-serdes/serdes-core.c index 6a8f7e1c18a4..da45502f254f 100644 --- a/drivers/mfd/display-serdes/serdes-core.c +++ b/drivers/mfd/display-serdes/serdes-core.c @@ -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; diff --git a/drivers/mfd/display-serdes/serdes-i2c.c b/drivers/mfd/display-serdes/serdes-i2c.c index 78b3097a50ea..feb2d8f3460c 100644 --- a/drivers/mfd/display-serdes/serdes-i2c.c +++ b/drivers/mfd/display-serdes/serdes-i2c.c @@ -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 "); MODULE_DESCRIPTION("display i2c interface for different serdes");