* [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages
@ 2019-09-17 9:43 Jiada Wang
2019-09-17 9:43 ` [PATCH v3 45/49] Input: atmel_mxt_ts: use gpiod_set_value_cansleep for reset pin Jiada Wang
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Jiada Wang @ 2019-09-17 9:43 UTC (permalink / raw)
To: nick, dmitry.torokhov, jikos, benjamin.tissoires, rydberg
Cc: linux-input, linux-kernel, jiada_wang
From: Deepak Das <deepak_das@mentor.com>
T44 object returns the count of valid T5 messages in the buffer. According
to atmel, this count should be the main criteria to read the number of T5
messages.
Following is the statement from atmel confirming the same :-
"For the readout of messages we recommend to stop after the last message
is read out from the buffer. One way to identify the amount of new messages
is to read T44. The other way is to monitor the /CHG line which indicates
independent of mode 0 or mode 1 if there are still data in the buffer.
0xFF indicates that there is no message pending anymore, but it is not
recommended to use this as the main criteria to control the
data transfer."
This commit modifies the logic to readout the T5 messages on the basis
of T44 object.
Signed-off-by: Deepak Das <deepak_das@mentor.com>
Signed-off-by: Sanjeev Chugh <sanjeev_chugh@mentor.com>
Signed-off-by: George G. Davis <george_davis@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 55 +++++++++++++++---------
1 file changed, 35 insertions(+), 20 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 93bb19cad7e1..76bda6137bf7 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1489,7 +1489,7 @@ static u8 mxt_max_msg_read_count(struct mxt_data *data, u8 max_T5_msg_count)
return min(T5_msg_count_limit, max_T5_msg_count);
}
-static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+static int mxt_process_messages_t44(struct mxt_data *data)
{
struct device *dev = &data->client->dev;
int ret;
@@ -1502,7 +1502,7 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
data->T5_msg_size + 1, data->msg_buf);
if (ret) {
dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
- return IRQ_NONE;
+ return ret;
}
T5_msg_count = data->msg_buf[0];
@@ -1512,7 +1512,7 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
* Mode 0. It results in unnecessary I2C operations but it is benign.
*/
if (!T5_msg_count)
- return IRQ_NONE;
+ return processed_valid;
if (T5_msg_count > data->max_reportid) {
dev_warn(dev, "T44 count %d exceeded max report id\n",
@@ -1524,12 +1524,14 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
ret = mxt_proc_message(data, data->msg_buf + 1);
if (ret < 0) {
dev_warn(dev, "Unexpected invalid message\n");
- return IRQ_NONE;
+ return ret;
}
total_pending = T5_msg_count - 1;
- if (!total_pending)
+ if (!total_pending) {
+ processed_valid = 1;
goto end;
+ }
/* Process remaining messages if necessary */
T5_msg_count = mxt_max_msg_read_count(data, total_pending);
@@ -1553,7 +1555,7 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
data->update_input = false;
}
- return IRQ_HANDLED;
+ return processed_valid;
}
static int mxt_process_messages_until_invalid(struct mxt_data *data)
@@ -1583,7 +1585,7 @@ static int mxt_process_messages_until_invalid(struct mxt_data *data)
return -EBUSY;
}
-static irqreturn_t mxt_process_messages(struct mxt_data *data)
+static int mxt_process_messages(struct mxt_data *data)
{
int total_handled, num_handled;
u8 count = data->last_message_count;
@@ -1594,7 +1596,7 @@ static irqreturn_t mxt_process_messages(struct mxt_data *data)
/* include final invalid message */
total_handled = mxt_read_and_process_messages(data, count + 1);
if (total_handled < 0)
- return IRQ_NONE;
+ return total_handled;
/* if there were invalid messages, then we are done */
else if (total_handled <= count)
goto update_count;
@@ -1603,7 +1605,7 @@ static irqreturn_t mxt_process_messages(struct mxt_data *data)
do {
num_handled = mxt_read_and_process_messages(data, 2);
if (num_handled < 0)
- return IRQ_NONE;
+ return num_handled;
total_handled += num_handled;
@@ -1619,12 +1621,13 @@ static irqreturn_t mxt_process_messages(struct mxt_data *data)
data->update_input = false;
}
- return IRQ_HANDLED;
+ return total_handled;
}
static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
struct mxt_data *data = dev_id;
+ int ret;
if (data->in_bootloader) {
complete(&data->chg_completion);
@@ -1632,17 +1635,22 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
if (data->flash && &data->flash->work)
cancel_delayed_work_sync(&data->flash->work);
- return IRQ_RETVAL(mxt_check_bootloader(data));
+ ret = mxt_check_bootloader(data);
+ return IRQ_RETVAL(ret);
}
if (!data->object_table)
return IRQ_HANDLED;
- if (data->T44_address) {
- return mxt_process_messages_t44(data);
- } else {
- return mxt_process_messages(data);
- }
+ if (data->T44_address)
+ ret = mxt_process_messages_t44(data);
+ else
+ ret = mxt_process_messages(data);
+
+ if (ret <= 0)
+ return IRQ_NONE;
+ else
+ return IRQ_HANDLED;
}
static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
@@ -1777,8 +1785,11 @@ static int mxt_acquire_irq(struct mxt_data *data)
}
if (data->object_table && data->use_retrigen_workaround) {
- error = mxt_process_messages_until_invalid(data);
- if (error)
+ if (data->T44_address)
+ error = mxt_process_messages_t44(data);
+ else
+ error = mxt_process_messages_until_invalid(data);
+ if (error < 0)
return error;
}
@@ -4032,8 +4043,12 @@ static int mxt_start(struct mxt_data *data)
* Discard any touch messages still in message buffer
* from before chip went to sleep
*/
- ret = mxt_process_messages_until_invalid(data);
- if (ret)
+
+ if (data->T44_address)
+ ret = mxt_process_messages_t44(data);
+ else
+ ret = mxt_process_messages_until_invalid(data);
+ if (ret < 0)
break;
ret = mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
--
2.19.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 45/49] Input: atmel_mxt_ts: use gpiod_set_value_cansleep for reset pin
2019-09-17 9:43 [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages Jiada Wang
@ 2019-09-17 9:43 ` Jiada Wang
2019-09-17 9:43 ` [PATCH v3 46/49] input: touchscreen: atmel_mxt_ts: Added sysfs entry for touchscreen status Jiada Wang
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jiada Wang @ 2019-09-17 9:43 UTC (permalink / raw)
To: nick, dmitry.torokhov, jikos, benjamin.tissoires, rydberg
Cc: linux-input, linux-kernel, jiada_wang
From: Balasubramani Vivekanandan <balasubramani_vivekanandan@mentor.com>
In case of remote display, touch controller will be also remote.
In such cases, the reset pin of the touch controller will be
controlled through bridging ICs like Deserilizer and Serializer.
Therefore accessing the gpio pins require transactions with the
external IC. Using the function gpiod_set_value will print a
warning like below
WARNING: CPU: 0 PID: 576 at drivers/gpio/gpiolib.c:1441 gpiod_set_value+0x34/0x60()
CPU: 0 PID: 576 Comm: modprobe Not tainted 3.14.79-08377-g84ea22f-dirty #4
Backtrace:
[<80011c58>] (dump_backtrace) from [<80011e60>] (show_stack+0x18/0x1c)
[<80011e48>] (show_stack) from [<8052d7ac>] (dump_stack+0x7c/0x9c)
[<8052d730>] (dump_stack) from [<800241bc>] (warn_slowpath_common+0x74/0x9c)
[<80024148>] (warn_slowpath_common) from [<80024288>] (warn_slowpath_null+0x24/0x2c)
[<80024264>] (warn_slowpath_null) from [<8029e070>] (gpiod_set_value+0x34/0x60)
[<8029e03c>] (gpiod_set_value) from [<7f492e98>] (mxt_probe+0x1e0/0x718 [atmel_mxt_ts])
[<7f492cb8>] (mxt_probe [atmel_mxt_ts]) from [<803c4d34>] (i2c_device_probe+0xcc/0xec)
[<803c4c68>] (i2c_device_probe) from [<803252a0>] (driver_probe_device+0xc0/0x200)
Signed-off-by: Balasubramani Vivekanandan <balasubramani_vivekanandan@mentor.com>
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
Signed-off-by: Sanjeev Chugh <sanjeev_chugh@mentor.com>
Signed-off-by: George G. Davis <george_davis@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 76bda6137bf7..8444f7292e29 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2490,7 +2490,7 @@ static void mxt_regulator_enable(struct mxt_data *data)
if (!data->reg_vdd || !data->reg_avdd)
return;
- gpiod_set_value(data->reset_gpio, 0);
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
error = regulator_enable(data->reg_vdd);
if (error)
@@ -2508,7 +2508,7 @@ static void mxt_regulator_enable(struct mxt_data *data)
* voltage
*/
msleep(MXT_REGULATOR_DELAY);
- gpiod_set_value(data->reset_gpio, 1);
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
msleep(MXT_CHG_DELAY);
retry_wait:
@@ -4314,7 +4314,7 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
disable_irq(data->irq);
} else if (data->reset_gpio) {
msleep(MXT_RESET_GPIO_TIME);
- gpiod_set_value(data->reset_gpio, 1);
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
msleep(MXT_RESET_INVALID_CHG);
} else {
dev_dbg(&client->dev,
--
2.19.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 46/49] input: touchscreen: atmel_mxt_ts: Added sysfs entry for touchscreen status
2019-09-17 9:43 [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages Jiada Wang
2019-09-17 9:43 ` [PATCH v3 45/49] Input: atmel_mxt_ts: use gpiod_set_value_cansleep for reset pin Jiada Wang
@ 2019-09-17 9:43 ` Jiada Wang
2019-09-17 9:43 ` [PATCH v3 47/49] input: atmel_mxt_ts: added sysfs interface to update atmel T38 data Jiada Wang
2019-09-17 9:43 ` [PATCH v3 48/49] Input: atmel_mxt_ts: Implement synchronization during various operation Jiada Wang
3 siblings, 0 replies; 5+ messages in thread
From: Jiada Wang @ 2019-09-17 9:43 UTC (permalink / raw)
To: nick, dmitry.torokhov, jikos, benjamin.tissoires, rydberg
Cc: linux-input, linux-kernel, jiada_wang
From: Naveen Chakka <Naveen.Chakka@in.bosch.com>
To know the current communication status of the touch controller during
runtime, sysfs interface is added
sysfs interface: /sys/class/i2c-dev/i2c-*/device/*/touch_dev_stat
Executing the above sysfs interface provides two output values
1)Status of the touch device
value 0 represents device is inactive
value 1 represents device is active
2)Error counter
value represents the number of times device in inactive since last read
Signed-off-by: Naveen Chakka <Naveen.Chakka@in.bosch.com>
Signed-off-by: Sanjeev Chugh <sanjeev_chugh@mentor.com>
Signed-off-by: George G. Davis <george_davis@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 109 +++++++++++++++++++++--
1 file changed, 102 insertions(+), 7 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 8444f7292e29..e67c29f0e0ca 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -25,6 +25,7 @@
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
+#include <linux/timer.h>
#include <asm/unaligned.h>
#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
@@ -222,6 +223,7 @@ enum t100_type {
#define MXT_CHG_DELAY 100 /* msec */
#define MXT_POWERON_DELAY 150 /* msec */
#define MXT_BOOTLOADER_WAIT 36E5 /* 1 minute */
+#define MXT_WATCHDOG_TIMEOUT 1000 /* msec */
/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
@@ -317,6 +319,12 @@ struct mxt_flash {
struct delayed_work work;
};
+struct mxt_statusinfo {
+ bool dev_status;
+ bool intp_triggered;
+ u32 error_count;
+};
+
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
@@ -372,6 +380,9 @@ struct mxt_data {
const char *pcfg_name;
const char *input_name;
struct mxt_flash *flash;
+ struct work_struct watchdog_work;
+ struct timer_list watchdog_timer;
+ struct mxt_statusinfo mxt_status;
/* Cached parameters from object table */
u16 T5_address;
@@ -1624,11 +1635,30 @@ static int mxt_process_messages(struct mxt_data *data)
return total_handled;
}
+static void mxt_start_wd_timer(struct mxt_data *data)
+{
+ mod_timer(&data->watchdog_timer, jiffies +
+ msecs_to_jiffies(MXT_WATCHDOG_TIMEOUT));
+}
+
+static void mxt_stop_wd_timer(struct mxt_data *data)
+{
+ /*
+ * Ensure we wait until the watchdog timer
+ * running on a different CPU finishes
+ */
+ del_timer_sync(&data->watchdog_timer);
+ cancel_work_sync(&data->watchdog_work);
+ del_timer_sync(&data->watchdog_timer);
+}
+
static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
struct mxt_data *data = dev_id;
int ret;
+ data->mxt_status.intp_triggered = true;
+
if (data->in_bootloader) {
complete(&data->chg_completion);
@@ -1636,21 +1666,25 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
cancel_delayed_work_sync(&data->flash->work);
ret = mxt_check_bootloader(data);
- return IRQ_RETVAL(ret);
+ ret = IRQ_RETVAL(ret);
+ goto exit;
}
- if (!data->object_table)
- return IRQ_HANDLED;
+ if (!data->object_table) {
+ ret = IRQ_HANDLED;
+ goto exit;
+ }
if (data->T44_address)
ret = mxt_process_messages_t44(data);
else
ret = mxt_process_messages(data);
- if (ret <= 0)
- return IRQ_NONE;
- else
- return IRQ_HANDLED;
+ ret = (ret <= 0) ? IRQ_NONE : IRQ_HANDLED;
+
+exit:
+ data->mxt_status.intp_triggered = false;
+ return ret;
}
static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
@@ -2970,6 +3004,36 @@ static int mxt_bootloader_status(struct mxt_data *data)
return 0;
}
+static void mxt_watchdog_timer(struct timer_list *t)
+{
+ struct mxt_data *data = from_timer(data, t, watchdog_timer);
+
+ if (!work_pending(&data->watchdog_work)) {
+ if (!data->mxt_status.intp_triggered)
+ schedule_work(&data->watchdog_work);
+ }
+
+ mxt_start_wd_timer(data);
+}
+
+static void mxt_watchdog_work(struct work_struct *work)
+{
+ struct mxt_data *data =
+ container_of(work, struct mxt_data, watchdog_work);
+ u16 info_buf;
+ int ret = 0;
+ u8 size = 2;
+
+ ret = __mxt_read_reg(data->client, 0, size, &info_buf);
+
+ if (ret) {
+ data->mxt_status.error_count++;
+ data->mxt_status.dev_status = false;
+ } else {
+ data->mxt_status.dev_status = true;
+ }
+}
+
static int mxt_initialize(struct mxt_data *data)
{
struct i2c_client *client = data->client;
@@ -3947,6 +4011,22 @@ static const struct attribute_group mxt_fw_attr_group = {
.attrs = mxt_fw_attrs,
};
+static ssize_t mxt_touch_device_status(struct device *dev, struct
+ device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (data->mxt_status.dev_status)
+ data->mxt_status.error_count = 0;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d %d\n", data->mxt_status.dev_status,
+ data->mxt_status.error_count);
+ /* clear the error counter once it is read */
+ data->mxt_status.error_count = 0;
+ return ret;
+}
+
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
@@ -3958,6 +4038,7 @@ static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL,
mxt_debug_v2_enable_store);
static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);
static DEVICE_ATTR(t25, 0600, mxt_t25_selftest_show, mxt_t25_selftest_store);
+static DEVICE_ATTR(touch_dev_stat, 0444, mxt_touch_device_status, NULL);
static struct attribute *mxt_attrs[] = {
&dev_attr_fw_version.attr,
@@ -3969,6 +4050,7 @@ static struct attribute *mxt_attrs[] = {
&dev_attr_debug_v2_enable.attr,
&dev_attr_debug_notify.attr,
&dev_attr_t25.attr,
+ &dev_attr_touch_dev_stat.attr,
NULL
};
@@ -4322,6 +4404,13 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
msleep(MXT_RESET_TIME);
}
+ INIT_WORK(&data->watchdog_work, mxt_watchdog_work);
+
+ /* setup watchdog timer */
+ timer_setup(&data->watchdog_timer, mxt_watchdog_timer, 0);
+
+ mxt_start_wd_timer(data);
+
error = mxt_initialize(data);
if (error)
goto err_free_object;
@@ -4336,8 +4425,11 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
return 0;
err_free_object:
+ cancel_work_sync(&data->watchdog_work);
+ mxt_stop_wd_timer(data);
mxt_free_input_device(data);
mxt_free_object_table(data);
+ del_timer(&data->watchdog_timer);
if (data->reset_gpio) {
sysfs_remove_link(&client->dev.kobj, "reset");
gpiod_unexport(data->reset_gpio);
@@ -4360,6 +4452,9 @@ static int mxt_remove(struct i2c_client *client)
mxt_free_input_device(data);
mxt_free_object_table(data);
+ cancel_work_sync(&data->watchdog_work);
+ mxt_stop_wd_timer(data);
+
return 0;
}
--
2.19.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 47/49] input: atmel_mxt_ts: added sysfs interface to update atmel T38 data
2019-09-17 9:43 [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages Jiada Wang
2019-09-17 9:43 ` [PATCH v3 45/49] Input: atmel_mxt_ts: use gpiod_set_value_cansleep for reset pin Jiada Wang
2019-09-17 9:43 ` [PATCH v3 46/49] input: touchscreen: atmel_mxt_ts: Added sysfs entry for touchscreen status Jiada Wang
@ 2019-09-17 9:43 ` Jiada Wang
2019-09-17 9:43 ` [PATCH v3 48/49] Input: atmel_mxt_ts: Implement synchronization during various operation Jiada Wang
3 siblings, 0 replies; 5+ messages in thread
From: Jiada Wang @ 2019-09-17 9:43 UTC (permalink / raw)
To: nick, dmitry.torokhov, jikos, benjamin.tissoires, rydberg
Cc: linux-input, linux-kernel, jiada_wang
From: Naveen Chakka <Naveen.Chakka@in.bosch.com>
Atmel touch controller contains T38 object where a user can store its own
data of length 64 bytes. T38 data will not be part of checksum
calculation on executing T6 BACKUP command.
format used to update the T38 data is given below:
<offset> <length> <actual_data>
offset: offset address of the data to be written in the t38 object
(in decimal)
length: length of the data to be written into the t38 object(in decimal)
data: actual data bytes to be written into the t38 object
(values should be in hex)
Ex:
1. 0 2 10 20
updates first two bytes of the t38 data with values 10 and 20
2. 19 6 10 2f 30 4a 50 60
updates 6 bytes of t38 data from the index 19-24 with hex values
Signed-off-by: Naveen Chakka <Naveen.Chakka@in.bosch.com>
Signed-off-by: Sanjeev Chugh <sanjeev_chugh@mentor.com>
Signed-off-by: George G. Davis <george_davis@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 102 +++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index e67c29f0e0ca..db4ad3b82650 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -4027,6 +4027,106 @@ static ssize_t mxt_touch_device_status(struct device *dev, struct
return ret;
}
+static ssize_t mxt_t38_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ size_t count = 0, size;
+ u8 i, *t38_buf;
+
+ if (!data->object_table)
+ return -ENXIO;
+
+ object = mxt_get_object(data, MXT_SPT_USERDATA_T38);
+ size = mxt_obj_size(object);
+
+ /* Pre-allocate buffer large enough to hold max size of t38 object.*/
+ t38_buf = kmalloc(size, GFP_KERNEL);
+ if (!t38_buf)
+ return -ENOMEM;
+
+ count = __mxt_read_reg(data->client, object->start_address,
+ size, t38_buf);
+ if (count)
+ goto end;
+
+ for (i = 0; i < size; i++)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "[%2u]: %02x\n", i, t38_buf[i]);
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+end:
+ kfree(t38_buf);
+ return count;
+}
+
+static ssize_t mxt_t38_data_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ ssize_t ret = 0, pos, offset;
+ unsigned int i, len, index;
+ u8 *t38_buf;
+
+ if (!data->object_table)
+ return -ENXIO;
+
+ object = mxt_get_object(data, MXT_SPT_USERDATA_T38);
+
+ /* Pre-allocate buffer large enough to hold max size of t38 object.*/
+ t38_buf = kmalloc(mxt_obj_size(object), GFP_KERNEL);
+ if (!t38_buf)
+ return -ENOMEM;
+
+ ret = sscanf(buf, "%zd %d%zd", &offset, &len, &pos);
+ if (ret != 2) {
+ dev_err(dev, "Bad format: Invalid parameter to update t38\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (len == 0) {
+ dev_err(dev,
+ "Bad format: Data length should not be equal to 0\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (offset < 0 || ((offset + len) > 64)) {
+ dev_err(dev, "Invalid offset value to update t38\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ index = pos;
+ for (i = 0; i < len; i++) {
+ ret = sscanf(buf + index, "%hhx%zd", t38_buf + i, &pos);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: Invalid Data\n");
+ ret = -EINVAL;
+ goto end;
+ }
+ index += pos;
+ }
+
+ ret = __mxt_write_reg(data->client, object->start_address + offset,
+ len, t38_buf);
+ if (ret)
+ goto end;
+
+ ret = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE,
+ true);
+ if (ret)
+ dev_err(dev, "backup command failed\n");
+ else
+ ret = count;
+end:
+ kfree(t38_buf);
+ return ret;
+}
+
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
@@ -4039,6 +4139,7 @@ static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL,
static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);
static DEVICE_ATTR(t25, 0600, mxt_t25_selftest_show, mxt_t25_selftest_store);
static DEVICE_ATTR(touch_dev_stat, 0444, mxt_touch_device_status, NULL);
+static DEVICE_ATTR(t38_data, 0600, mxt_t38_data_show, mxt_t38_data_store);
static struct attribute *mxt_attrs[] = {
&dev_attr_fw_version.attr,
@@ -4051,6 +4152,7 @@ static struct attribute *mxt_attrs[] = {
&dev_attr_debug_notify.attr,
&dev_attr_t25.attr,
&dev_attr_touch_dev_stat.attr,
+ &dev_attr_t38_data.attr,
NULL
};
--
2.19.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 48/49] Input: atmel_mxt_ts: Implement synchronization during various operation
2019-09-17 9:43 [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages Jiada Wang
` (2 preceding siblings ...)
2019-09-17 9:43 ` [PATCH v3 47/49] input: atmel_mxt_ts: added sysfs interface to update atmel T38 data Jiada Wang
@ 2019-09-17 9:43 ` Jiada Wang
3 siblings, 0 replies; 5+ messages in thread
From: Jiada Wang @ 2019-09-17 9:43 UTC (permalink / raw)
To: nick, dmitry.torokhov, jikos, benjamin.tissoires, rydberg
Cc: linux-input, linux-kernel, jiada_wang
From: Sanjeev Chugh <sanjeev_chugh@mentor.com>
There could be scope of race conditions when sysfs is being handled
and at the same time, device removal is occurring. For example,
we don't want the device removal to begin if the Atmel device
cfg update is going on or firmware update is going on. In such
cases, wait for device update to be completed before the removal
continues.
Thread Thread 2:
========================= =========================
mxt_update_fw_store() mxt_remove()
mutex_lock(&data->lock) ...
mxt_initialize() //Tries to acquire lock
request_firmware_nowait() mutex_lock(&data->lock)
... ==>waits for lock()
... .
... .
mutex_unlock(&data->lock) .
//Gets lock and proceeds
mxt_free_input_device();
...
mutex_unlock(&data->lock)
//Frees atmel driver data
kfree(data)
If the request_firmware_nowait() completes after the driver removal,
and callback is triggered. But kernel crashes since the module is
already removed.
This commit adds state machine to serialize such scenarios.
Signed-off-by: Sanjeev Chugh <sanjeev_chugh@mentor.com>
Signed-off-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 222 ++++++++++++++++++++---
1 file changed, 196 insertions(+), 26 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index db4ad3b82650..ff6d3ed58604 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -224,6 +224,7 @@ enum t100_type {
#define MXT_POWERON_DELAY 150 /* msec */
#define MXT_BOOTLOADER_WAIT 36E5 /* 1 minute */
#define MXT_WATCHDOG_TIMEOUT 1000 /* msec */
+#define MXT_CONFIG_TIMEOUT 1000 /* msec */
/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
@@ -247,6 +248,20 @@ enum t100_type {
#define DEBUG_MSG_MAX 200
+enum device_state {
+ MXT_STATE_READY,
+ MXT_STATE_UPDATING_CONFIG,
+ MXT_STATE_UPDATING_CONFIG_ASYNC,
+ MXT_STATE_START,
+ MXT_STATE_STOP,
+ MXT_STATE_GOING_AWAY
+};
+
+enum mxt_cmd {
+ UPDATE_CFG,
+ UPDATE_FW
+};
+
struct mxt_info {
u8 family_id;
u8 variant_id;
@@ -426,11 +441,15 @@ struct mxt_data {
/* Indicates whether device is in suspend */
bool suspended;
- /* Indicates whether device is updating configuration */
- bool updating_config;
+ struct mutex lock;
unsigned int mtu;
bool t25_status;
+
+ /* State handling for probe/remove, open/close and config update */
+ enum device_state e_state;
+
+ struct completion update_cfg_completion;
};
struct mxt_vb2_buffer {
@@ -1657,6 +1676,7 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
struct mxt_data *data = dev_id;
int ret;
+ mutex_lock(&data->lock);
data->mxt_status.intp_triggered = true;
if (data->in_bootloader) {
@@ -1684,6 +1704,8 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
exit:
data->mxt_status.intp_triggered = false;
+ mutex_unlock(&data->lock);
+
return ret;
}
@@ -2264,6 +2286,8 @@ static void mxt_free_object_table(struct mxt_data *data)
video_unregister_device(&data->dbg.vdev);
v4l2_device_unregister(&data->dbg.v4l2);
#endif
+ mutex_lock(&data->lock);
+
data->object_table = NULL;
kfree(data->info);
data->info = NULL;
@@ -2293,6 +2317,8 @@ static void mxt_free_object_table(struct mxt_data *data)
data->T100_reportid_min = 0;
data->T100_reportid_max = 0;
data->max_reportid = 0;
+
+ mutex_unlock(&data->lock);
}
static int mxt_parse_object_table(struct mxt_data *data,
@@ -2974,8 +3000,15 @@ static int mxt_configure_objects(struct mxt_data *data,
static void mxt_config_cb(const struct firmware *cfg, void *ctx)
{
+ struct mxt_data *data = ctx;
+
mxt_configure_objects(ctx, cfg);
release_firmware(cfg);
+ complete(&data->update_cfg_completion);
+ mutex_lock(&data->lock);
+ if (data->e_state != MXT_STATE_GOING_AWAY)
+ data->e_state = MXT_STATE_READY;
+ mutex_unlock(&data->lock);
}
static int mxt_bootloader_status(struct mxt_data *data)
@@ -3088,6 +3121,15 @@ static int mxt_initialize(struct mxt_data *data)
goto err_free_sysfs;
if (data->cfg_name) {
+ mutex_lock(&data->lock);
+ if (data->e_state != MXT_STATE_GOING_AWAY) {
+ data->e_state = MXT_STATE_UPDATING_CONFIG_ASYNC;
+ } else {
+ mutex_unlock(&data->lock);
+ return -EBUSY;
+ }
+ reinit_completion(&data->update_cfg_completion);
+ mutex_unlock(&data->lock);
error = request_firmware_nowait(THIS_MODULE, true,
data->cfg_name,
&client->dev,
@@ -3867,30 +3909,58 @@ static int mxt_update_file_name(struct device *dev, char **file_name,
return 0;
}
+static int mxt_process_operation(struct mxt_data *data,
+ enum mxt_cmd cmd,
+ void *cmd_data);
+
static ssize_t mxt_update_fw_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct mxt_data *data = dev_get_drvdata(dev);
+ char *filename = NULL;
+ int ret;
+
+ ret = mxt_update_file_name(dev, &filename, buf, count);
+ if (ret)
+ goto out;
+
+ ret = mxt_process_operation(data, UPDATE_FW, filename);
+ kfree(filename);
+
+ if (ret)
+ goto out;
+
+ return count;
+out:
+ return ret;
+}
+
+static int mxt_fw_update(struct mxt_data *data,
+ const char *filename)
+{
+ struct device *dev = &data->client->dev;
+ unsigned int len = 0;
int error;
- error = mxt_update_file_name(dev, &data->fw_name, buf, count);
+ len = strlen(filename);
+ error = mxt_update_file_name(dev, &data->fw_name, filename, len);
if (error)
return error;
error = mxt_load_fw(dev);
if (error) {
dev_err(dev, "The firmware update failed(%d)\n", error);
- count = error;
- } else {
- dev_info(dev, "The firmware update succeeded\n");
-
- error = mxt_initialize(data);
- if (error)
- return error;
+ return error;
}
- return count;
+ error = mxt_initialize(data);
+ if (error)
+ return error;
+
+ dev_info(dev, "The firmware update succeeded\n");
+
+ return error;
}
static ssize_t mxt_update_cfg_store(struct device *dev,
@@ -3898,14 +3968,38 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
const char *buf, size_t count)
{
struct mxt_data *data = dev_get_drvdata(dev);
+ char *filename = NULL;
+ int ret;
+
+ ret = mxt_update_file_name(dev, &filename, buf, count);
+ if (ret)
+ goto out;
+
+ ret = mxt_process_operation(data, UPDATE_CFG, filename);
+ kfree(filename);
+
+ if (ret)
+ goto out;
+
+ return count;
+out:
+ return ret;
+}
+
+static int mxt_cfg_update(struct mxt_data *data,
+ char *filename)
+{
+ struct device *dev = &data->client->dev;
const struct firmware *cfg;
+ unsigned int len = 0;
int ret;
- ret = mxt_update_file_name(dev, &data->cfg_name, buf, count);
+ len = strlen(filename);
+ ret = mxt_update_file_name(dev, &data->cfg_name, filename, len);
if (ret)
return ret;
- ret = request_firmware(&cfg, data->cfg_name, dev);
+ ret = request_firmware(&cfg, data->cfg_name, &data->client->dev);
if (ret < 0) {
dev_err(dev, "Failure to request config file %s\n",
data->cfg_name);
@@ -3913,8 +4007,6 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
goto out;
}
- data->updating_config = true;
-
mxt_free_input_device(data);
if (data->suspended) {
@@ -3930,15 +4022,8 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
}
ret = mxt_configure_objects(data, cfg);
- if (ret)
- goto release;
-
- ret = count;
-
-release:
release_firmware(cfg);
out:
- data->updating_config = false;
return ret;
}
@@ -4202,8 +4287,17 @@ static int mxt_start(struct mxt_data *data)
{
int ret = 0;
- if (!data->suspended || data->in_bootloader)
+ mutex_lock(&data->lock);
+ if (!data->suspended) {
+ mutex_unlock(&data->lock);
return 0;
+ }
+ if (data->in_bootloader || data->e_state != MXT_STATE_READY) {
+ mutex_unlock(&data->lock);
+ return -EBUSY;
+ }
+ data->e_state = MXT_STATE_START;
+ mutex_unlock(&data->lock);
switch (data->suspend_mode) {
case MXT_SUSPEND_T9_CTRL:
@@ -4247,8 +4341,12 @@ static int mxt_start(struct mxt_data *data)
ret = mxt_acquire_irq(data);
}
+ mutex_lock(&data->lock);
if (!ret)
data->suspended = false;
+ if (data->e_state != MXT_STATE_GOING_AWAY)
+ data->e_state = MXT_STATE_READY;
+ mutex_unlock(&data->lock);
return ret;
}
@@ -4257,8 +4355,19 @@ static int mxt_stop(struct mxt_data *data)
{
int ret;
- if (data->suspended || data->in_bootloader || data->updating_config)
+ mutex_lock(&data->lock);
+ if (data->suspended) {
+ mutex_unlock(&data->lock);
return 0;
+ }
+ if (data->in_bootloader || (data->e_state != MXT_STATE_READY &&
+ data->e_state != MXT_STATE_GOING_AWAY)) {
+ mutex_unlock(&data->lock);
+ return -EBUSY;
+ }
+ if (data->e_state != MXT_STATE_GOING_AWAY)
+ data->e_state = MXT_STATE_STOP;
+ mutex_unlock(&data->lock);
switch (data->suspend_mode) {
case MXT_SUSPEND_T9_CTRL:
@@ -4288,8 +4397,15 @@ static int mxt_stop(struct mxt_data *data)
break;
}
+ mutex_lock(&data->lock);
data->suspended = true;
+
+ if (data->e_state != MXT_STATE_GOING_AWAY)
+ data->e_state = MXT_STATE_READY;
+ mutex_unlock(&data->lock);
+
return 0;
+
}
static int mxt_input_open(struct input_dev *dev)
@@ -4444,12 +4560,15 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
client->adapter->nr, client->addr);
+ mutex_init(&data->lock);
+
data->client = client;
i2c_set_clientdata(client, data);
init_completion(&data->chg_completion);
init_completion(&data->reset_completion);
init_completion(&data->crc_completion);
+ init_completion(&data->update_cfg_completion);
mutex_init(&data->debug_msg_lock);
data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
@@ -4543,6 +4662,18 @@ static int mxt_remove(struct i2c_client *client)
{
struct mxt_data *data = i2c_get_clientdata(client);
+ mutex_lock(&data->lock);
+ if (data->e_state == MXT_STATE_UPDATING_CONFIG_ASYNC ||
+ data->e_state == MXT_STATE_UPDATING_CONFIG) {
+ data->e_state = MXT_STATE_GOING_AWAY;
+ mutex_unlock(&data->lock);
+ mxt_wait_for_completion(data, &data->update_cfg_completion,
+ MXT_CONFIG_TIMEOUT);
+ } else {
+ data->e_state = MXT_STATE_GOING_AWAY;
+ mutex_unlock(&data->lock);
+ }
+
disable_irq(data->irq);
sysfs_remove_group(&client->dev.kobj, &mxt_fw_attr_group);
if (data->reset_gpio) {
@@ -4599,6 +4730,45 @@ static int __maybe_unused mxt_resume(struct device *dev)
return ret;
}
+static int mxt_process_operation(struct mxt_data *data,
+ enum mxt_cmd cmd,
+ void *cmd_data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->lock);
+ if (data->e_state != MXT_STATE_READY) {
+ mutex_unlock(&data->lock);
+ dev_err(&data->client->dev, "Atmel touch device is shutting down\n");
+ return -EBUSY;
+ }
+ data->e_state = MXT_STATE_UPDATING_CONFIG;
+ reinit_completion(&data->update_cfg_completion);
+ mutex_unlock(&data->lock);
+
+ switch (cmd) {
+ case UPDATE_CFG:
+ case UPDATE_FW:
+ if (cmd == UPDATE_CFG)
+ ret = mxt_cfg_update(data, (char *)cmd_data);
+ else
+ ret = mxt_fw_update(data, (char *)cmd_data);
+ break;
+
+ default:
+ break;
+ }
+ mutex_lock(&data->lock);
+ if (data->e_state != MXT_STATE_UPDATING_CONFIG_ASYNC) {
+ complete(&data->update_cfg_completion);
+ if (data->e_state != MXT_STATE_GOING_AWAY)
+ data->e_state = MXT_STATE_READY;
+ }
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
static const struct of_device_id mxt_of_match[] = {
--
2.19.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2019-09-17 9:43 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-09-17 9:43 [PATCH v3 44/49] Input: Atmel: use T44 object to process T5 messages Jiada Wang
2019-09-17 9:43 ` [PATCH v3 45/49] Input: atmel_mxt_ts: use gpiod_set_value_cansleep for reset pin Jiada Wang
2019-09-17 9:43 ` [PATCH v3 46/49] input: touchscreen: atmel_mxt_ts: Added sysfs entry for touchscreen status Jiada Wang
2019-09-17 9:43 ` [PATCH v3 47/49] input: atmel_mxt_ts: added sysfs interface to update atmel T38 data Jiada Wang
2019-09-17 9:43 ` [PATCH v3 48/49] Input: atmel_mxt_ts: Implement synchronization during various operation Jiada Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).