From: Marcel Grosshans <MarcelViktor.Grosshans@xxxxxxxxxxxx> Unexpected power interruption or reset of the touch controller may disable touch panel function. To avoid this situation, the touch controller is completely reinitialized if BOOT_COMPLETE notification occurs. To make it possible we process reinitialization in a separate queue. Signed-off-by: Marcel Grosshans <MarcelViktor.Grosshans@xxxxxxxxxxxx> Signed-off-by: Knut Wohlrab <Knut.Wohlrab@xxxxxxxxxxxx> Signed-off-by: Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> Signed-off-by: Dirk Behme <dirk.behme@xxxxxxxxxxxx> --- drivers/input/touchscreen/zforce_ts.c | 127 +++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 25 deletions(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 9bbadaa..0c08220 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -31,6 +31,7 @@ #include <linux/platform_data/zforce_ts.h> #include <linux/regulator/consumer.h> #include <linux/of.h> +#include <linux/workqueue.h> #define WAIT_TIMEOUT msecs_to_jiffies(1000) @@ -98,6 +99,12 @@ struct zforce_point { int prblty; }; +enum zforce_state { + ZF_STATE_UNINITIALZED = 0, + ZF_STATE_PROBE_COMPLETE, + ZF_STATE_DEV_OPENED, +}; + /* * @client the i2c_client * @input the input device @@ -138,6 +145,11 @@ struct zforce_ts { struct mutex command_mutex; int command_waiting; int command_result; + + struct work_struct ts_workq; + int notification; + + enum zforce_state state; }; static int zforce_command(struct zforce_ts *ts, u8 cmd) @@ -188,6 +200,7 @@ static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len) buf[1], buf[2]); ts->command_waiting = buf[2]; + reinit_completion(&ts->command_done); mutex_lock(&ts->access_mutex); ret = i2c_master_send(client, buf, len); @@ -471,6 +484,15 @@ static void zforce_complete(struct zforce_ts *ts, int cmd, int result) dev_dbg(&client->dev, "completing command 0x%x\n", cmd); ts->command_result = result; complete(&ts->command_done); + } else if (cmd == NOTIFICATION_BOOTCOMPLETE) { + dev_dbg(&client->dev, "got notification 0x%x\n", cmd); + + /* abourt previous waiting command if any available */ + ts->command_result = -ECONNABORTED; + ts->notification = cmd; + complete(&ts->command_done); + + queue_work(system_long_wq, &ts->ts_workq); } else { dev_dbg(&client->dev, "command %d not for us\n", cmd); } @@ -596,11 +618,85 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * This device is used in automotive environment. In this + * we should never fail. Some connection issues caused by vibration + * should be ignored and can be recoverable. + */ +static void zforce_boot(struct zforce_ts *ts) +{ + struct device *dev = &ts->client->dev; + int ret; + + /* need to start device to get version information */ + ret = zforce_command_wait(ts, COMMAND_INITIALIZE); + if (ret) + dev_err(dev, "unable to initialize, %d\n", ret); + + switch (ts->state) { + case ZF_STATE_UNINITIALZED: + ret = zforce_command_wait(ts, COMMAND_STATUS); + if (ret) + dev_err(dev, "couldn't get status, %d\n", ret); + /* fallthrough, we need zforce_stop to complete. */ + case ZF_STATE_PROBE_COMPLETE: + /* stop device and put it into sleep until it is opened */ + ret = zforce_stop(ts); + if (ret) + dev_err(dev, "couldn't stop zforce, %d\n", ret); + + ts->state = ZF_STATE_PROBE_COMPLETE; + break; + case ZF_STATE_DEV_OPENED: + ret = zforce_start(ts); + if (ret) + dev_err(dev, "Failed to restart, %d\n", ret); + break; + } +} + +static void zforce_notification_queue(struct work_struct *work) +{ + struct zforce_ts *ts = container_of(work, struct zforce_ts, ts_workq); + struct i2c_client *client = ts->client; + struct input_dev *input = ts->input; + + if (device_may_wakeup(&client->dev)) { + if (!ts->suspending) + pm_stay_awake(&client->dev); + else + pm_wakeup_event(&client->dev, 500); + } + + mutex_lock(&input->mutex); + + switch (ts->notification) { + case NOTIFICATION_BOOTCOMPLETE: + zforce_boot(ts); + break; + default: + dev_err(&client->dev, + "unknown notification: %#x\n", ts->notification); + } + + mutex_unlock(&input->mutex); + + if (!ts->suspending && device_may_wakeup(&client->dev)) + pm_relax(&client->dev); +} + static int zforce_input_open(struct input_dev *dev) { struct zforce_ts *ts = input_get_drvdata(dev); + int ret; + + ret = zforce_start(ts); + if (ret) + return ret; - return zforce_start(ts); + ts->state = ZF_STATE_DEV_OPENED; + + return 0; } static void zforce_input_close(struct input_dev *dev) @@ -613,6 +709,8 @@ static void zforce_input_close(struct input_dev *dev) if (ret) dev_warn(&client->dev, "stopping zforce failed\n"); + ts->state = ZF_STATE_PROBE_COMPLETE; + return; } @@ -875,6 +973,7 @@ static int zforce_probe(struct i2c_client *client, input_set_drvdata(ts->input, ts); init_completion(&ts->command_done); + INIT_WORK(&ts->ts_workq, zforce_notification_queue); /* * The zforce pulls the interrupt low when it has data ready. @@ -894,33 +993,11 @@ static int zforce_probe(struct i2c_client *client, i2c_set_clientdata(client, ts); + ts->state = ZF_STATE_UNINITIALZED; + /* let the controller boot */ zforce_reset_deassert(ts); - ts->command_waiting = NOTIFICATION_BOOTCOMPLETE; - if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) - dev_warn(&client->dev, "bootcomplete timed out\n"); - - /* need to start device to get version information */ - ret = zforce_command_wait(ts, COMMAND_INITIALIZE); - if (ret) { - dev_err(&client->dev, "unable to initialize, %d\n", ret); - return ret; - } - - /* this gets the firmware version among other information */ - ret = zforce_command_wait(ts, COMMAND_STATUS); - if (ret < 0) { - dev_err(&client->dev, "couldn't get status, %d\n", ret); - zforce_stop(ts); - return ret; - } - - /* stop device and put it into sleep until it is opened */ - ret = zforce_stop(ts); - if (ret < 0) - return ret; - device_set_wakeup_capable(&client->dev, true); ret = input_register_device(input_dev); -- 2.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html