mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
mfd: display-serdes: add error detection and recovery function
Signed-off-by: Luo Wei <lw@rock-chips.com> Change-Id: I5cf255819541e296e23834e4dc1f5b1fa204c0e6
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -351,6 +352,7 @@ struct serdes {
|
||||
struct mutex io_lock;
|
||||
struct mutex irq_lock;
|
||||
struct mutex wq_lock;
|
||||
struct mutex reg_check_lock;
|
||||
struct device *dev;
|
||||
enum serdes_type type;
|
||||
struct regmap *regmap;
|
||||
@@ -377,6 +379,10 @@ struct serdes {
|
||||
bool route_enable;
|
||||
bool use_delay_work;
|
||||
|
||||
struct kthread_worker *kworker;
|
||||
struct kthread_delayed_work reg_check_work;
|
||||
bool use_reg_check_work;
|
||||
|
||||
bool split_mode_enable;
|
||||
unsigned int reg_hw;
|
||||
unsigned int reg_use;
|
||||
@@ -391,6 +397,7 @@ struct serdes {
|
||||
struct pinctrl_state *pins_sleep;
|
||||
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
struct serdes_init_seq *serdes_backup_seq;
|
||||
struct serdes_bridge *serdes_bridge;
|
||||
struct serdes_bridge_split *serdes_bridge_split;
|
||||
struct serdes_panel *serdes_panel;
|
||||
|
||||
@@ -14,7 +14,7 @@ static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT];
|
||||
int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
{
|
||||
struct device *dev = serdes->dev;
|
||||
int i, ret = 0;
|
||||
int i, num = 0, ret = 0;
|
||||
unsigned int def = 0;
|
||||
|
||||
for (i = 0; i < serdes->serdes_init_seq->reg_seq_cnt; i++) {
|
||||
@@ -30,9 +30,9 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
serdes->serdes_init_seq->reg_sequence[i].def);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(serdes->dev,
|
||||
"failed to write register %04x, ret %d, write again now\n",
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg, ret);
|
||||
SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n",
|
||||
serdes->dev,
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg, ret);
|
||||
ret = serdes_reg_write(serdes,
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg,
|
||||
serdes->serdes_init_seq->reg_sequence[i].def);
|
||||
@@ -40,9 +40,10 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
serdes_reg_read(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, &def);
|
||||
if ((def != serdes->serdes_init_seq->reg_sequence[i].def) || (ret < 0)) {
|
||||
/* if read value != write value then write again */
|
||||
dev_err(dev, "read %04x %04x != %04x\n",
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg,
|
||||
def, serdes->serdes_init_seq->reg_sequence[i].def);
|
||||
if (num++ < 1)
|
||||
dev_err(dev, "read %04x %04x != %04x\n",
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg,
|
||||
def, serdes->serdes_init_seq->reg_sequence[i].def);
|
||||
serdes_reg_write(serdes,
|
||||
serdes->serdes_init_seq->reg_sequence[i].reg,
|
||||
serdes->serdes_init_seq->reg_sequence[i].def);
|
||||
@@ -91,6 +92,133 @@ static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_i2c_set_sequence_backup(struct serdes *serdes)
|
||||
{
|
||||
struct device *dev = serdes->dev;
|
||||
int i, num = 0, ret = 0;
|
||||
unsigned int def = 0;
|
||||
|
||||
for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) {
|
||||
if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff) {
|
||||
SERDES_DBG_MFD("%s: delay 0x%04x us\n", __func__,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
udelay(serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = serdes_reg_write(serdes,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].reg,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
if (ret < 0) {
|
||||
SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n",
|
||||
serdes->dev,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].reg, ret);
|
||||
ret = serdes_reg_write(serdes,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].reg,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
}
|
||||
serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg, &def);
|
||||
if ((def != serdes->serdes_backup_seq->reg_sequence[i].def) || (ret < 0)) {
|
||||
/* if read value != write value then write again */
|
||||
if (num++ < 1)
|
||||
dev_err(dev, "%s read %04x %04x != %04x\n", __func__,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].reg,
|
||||
def, serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
serdes_reg_write(serdes,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].reg,
|
||||
serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_i2c_backup_register(struct serdes *serdes)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) {
|
||||
if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff)
|
||||
continue;
|
||||
serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg,
|
||||
&serdes->serdes_backup_seq->reg_sequence[i].def);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_i2c_check_register(struct serdes *serdes, int *flag)
|
||||
{
|
||||
struct device *dev = serdes->dev;
|
||||
int ret = 0;
|
||||
unsigned int def = 0;
|
||||
unsigned int num = 0;
|
||||
|
||||
get_random_bytes(&num, 1);
|
||||
if (num > serdes->serdes_backup_seq->reg_seq_cnt - 1)
|
||||
num = 0;
|
||||
|
||||
if (serdes->serdes_backup_seq->reg_sequence[num].reg == 0xffff)
|
||||
return 0;
|
||||
|
||||
ret = serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[num].reg, &def);
|
||||
if ((def != serdes->serdes_backup_seq->reg_sequence[num].def) || (ret < 0)) {
|
||||
/* if read value != write value then write again */
|
||||
dev_err(dev, "%s read %04x %04x != %04x\n", __func__,
|
||||
serdes->serdes_backup_seq->reg_sequence[num].reg,
|
||||
def, serdes->serdes_backup_seq->reg_sequence[num].def);
|
||||
*flag = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serdes_reg_check_work(struct kthread_work *work)
|
||||
{
|
||||
int flag = 0;
|
||||
struct serdes *serdes = container_of(work, struct serdes,
|
||||
reg_check_work.work);
|
||||
|
||||
if (atomic_read(&serdes->flag_ser_init)) {
|
||||
serdes_i2c_backup_register(serdes);
|
||||
atomic_set(&serdes->flag_ser_init, 0);
|
||||
}
|
||||
|
||||
serdes_i2c_check_register(serdes, &flag);
|
||||
if (flag) {
|
||||
if (serdes->chip_data->chip_init)
|
||||
serdes->chip_data->chip_init(serdes);
|
||||
serdes_i2c_set_sequence_backup(serdes);
|
||||
msleep(500);
|
||||
SERDES_DBG_MFD("%s %s\n", __func__, serdes->chip_data->name);
|
||||
}
|
||||
kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work,
|
||||
msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
static int serdes_reg_check_work_setup(struct serdes *serdes)
|
||||
{
|
||||
kthread_init_delayed_work(&serdes->reg_check_work,
|
||||
serdes_reg_check_work);
|
||||
|
||||
serdes->kworker = kthread_create_worker(0, "%s", dev_name(serdes->dev));
|
||||
if (IS_ERR(serdes->kworker))
|
||||
return PTR_ERR(serdes->kworker);
|
||||
mutex_init(&serdes->reg_check_lock);
|
||||
atomic_set(&serdes->flag_ser_init, 1);
|
||||
kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work,
|
||||
msecs_to_jiffies(20000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serdes_reg_check_work_free(struct serdes *serdes)
|
||||
{
|
||||
kthread_cancel_delayed_work_sync(&serdes->reg_check_work);
|
||||
kthread_destroy_worker(serdes->kworker);
|
||||
}
|
||||
|
||||
static void serdes_mfd_work(struct work_struct *work)
|
||||
{
|
||||
struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work);
|
||||
@@ -161,6 +289,17 @@ static int serdes_get_init_seq(struct serdes *serdes)
|
||||
return err;
|
||||
}
|
||||
|
||||
serdes->serdes_backup_seq = devm_kzalloc(dev, sizeof(*serdes->serdes_backup_seq),
|
||||
GFP_KERNEL);
|
||||
if (!serdes->serdes_backup_seq)
|
||||
return -ENOMEM;
|
||||
|
||||
err = serdes_parse_init_seq(dev, data, len, serdes->serdes_backup_seq);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to parse serdes-init-sequence\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* init ser register(not des register) more early if uboot logo disabled */
|
||||
serdes->route_enable = of_property_read_bool(dev->of_node, "route-enable");
|
||||
if ((!serdes->route_enable) && (serdes->chip_data->serdes_type == TYPE_SER)) {
|
||||
@@ -284,6 +423,13 @@ static int serdes_i2c_probe(struct i2c_client *client,
|
||||
SERDES_DBG_MFD("%s: use_delay_work=%d\n", __func__, serdes->use_delay_work);
|
||||
}
|
||||
|
||||
serdes->use_reg_check_work = of_property_read_bool(dev->of_node, "use-reg-check-work");
|
||||
if (serdes->use_reg_check_work) {
|
||||
serdes_reg_check_work_setup(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: use_reg_check_work=%d\n", __func__, serdes->use_reg_check_work);
|
||||
}
|
||||
|
||||
dev_info(dev, "serdes %s serdes_i2c_probe successful version %s\n",
|
||||
serdes->chip_data->name, MFD_SERDES_DISPLAY_VERSION);
|
||||
|
||||
@@ -298,6 +444,20 @@ static void serdes_i2c_shutdown(struct i2c_client *client)
|
||||
serdes_device_shutdown(serdes);
|
||||
}
|
||||
|
||||
static void serdes_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct serdes *serdes = dev_get_drvdata(dev);
|
||||
|
||||
if (serdes->use_reg_check_work)
|
||||
serdes_reg_check_work_free(serdes);
|
||||
|
||||
if (serdes->use_delay_work) {
|
||||
cancel_delayed_work_sync(&serdes->mfd_delay_work);
|
||||
destroy_workqueue(serdes->mfd_wq);
|
||||
}
|
||||
}
|
||||
|
||||
static int serdes_i2c_prepare(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
@@ -394,6 +554,7 @@ static struct i2c_driver serdes_i2c_driver = {
|
||||
},
|
||||
.probe = serdes_i2c_probe,
|
||||
.shutdown = serdes_i2c_shutdown,
|
||||
.remove = (void *)serdes_i2c_remove,
|
||||
};
|
||||
|
||||
static int __init serdes_i2c_init(void)
|
||||
|
||||
Reference in New Issue
Block a user