From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dirk Behme Subject: [PATCH v2 1/4] Input: zforce_ts: Reinitialize touch controller when BOOT_COMPLETE received Date: Tue, 3 May 2016 12:41:47 +0200 Message-ID: <1462272110-24610-2-git-send-email-dirk.behme@de.bosch.com> References: <1462272110-24610-1-git-send-email-dirk.behme@de.bosch.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: Received: from smtp6-v.fe.bosch.de ([139.15.237.11]:47305 "EHLO smtp6-v.fe.bosch.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755901AbcECKlz (ORCPT ); Tue, 3 May 2016 06:41:55 -0400 Received: from vsmta12.fe.internet.bosch.com (unknown [10.4.98.52]) by imta24.fe.bosch.de (Postfix) with ESMTP id 3455AD80221 for ; Tue, 3 May 2016 12:41:54 +0200 (CEST) Received: from SI-HUB1000.de.bosch.com (vsgw22.fe.internet.bosch.com [10.4.98.11]) by vsmta12.fe.internet.bosch.com (Postfix) with ESMTP id D81591B8056D for ; Tue, 3 May 2016 12:41:53 +0200 (CEST) In-Reply-To: <1462272110-24610-1-git-send-email-dirk.behme@de.bosch.com> Sender: linux-input-owner@vger.kernel.org List-Id: linux-input@vger.kernel.org To: linux-input@vger.kernel.org, Dmitry Torokhov , Henrik Rydberg , Javier Martinez Canillas Cc: Marcel Grosshans , Knut Wohlrab , Oleksij Rempel From: Marcel Grosshans 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 occures. To make it possible we process reinitialization in a separate queue. Signed-off-by: Marcel Grosshans Signed-off-by: Knut Wohlrab Signed-off-by: Oleksij Rempel --- 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 7b3845a..9839d86 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -31,6 +31,7 @@ #include #include #include +#include #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); -- 1.9.1