diff mbox series

Input: atmel_mxt_ts: Add selftest support

Message ID 1624612727-31762-1-git-send-email-loic.poulain@linaro.org (mailing list archive)
State New, archived
Headers show
Series Input: atmel_mxt_ts: Add selftest support | expand

Commit Message

Loic Poulain June 25, 2021, 9:18 a.m. UTC
Add selftest support via T25 function. Selftest is exposed as a sysfs
attribute which can be written with a test code and read to get the
test result (PASS or FAIL).

The supported test codes seem to be device specific, so refer to the
device documentation for getting an exhausive list.

So far I've validated the following tests:
Signal limit test: code 0x17
AVdd test: code 0x01
All tests: code 0xfe
pin-fault test: code 0x12 (or 0x11)

Example:
$ echo 0x17 > /sys/bus/i2c/devices/*/selftest
$ cat /sys/bus/i2c/devices/*/selftest
PASS

Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 133 +++++++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)
diff mbox series

Patch

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 05de92c..12dfeb8 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -222,6 +222,13 @@  enum t100_type {
 
 #define MXT_PIXELS_PER_MM	20
 
+#define MXT_T25_RESULT_STATUS_SUCCESS   0xFE
+#define MXT_T25_RESULT_STATUS_INVALID   0xFD
+#define MXT_T25_RESULT_STATUS_NOAVDD    0x01
+#define MXT_T25_RESULT_STATUS_PINFAULT  0x11
+#define MXT_T25_RESULT_STATUS_PINFAULT2 0x12
+#define MXT_T25_RESULT_STATUS_SIGFAULT  0x17
+
 struct mxt_info {
 	u8 family_id;
 	u8 variant_id;
@@ -266,6 +273,11 @@  enum mxt_suspend_mode {
 	MXT_SUSPEND_T9_CTRL	= 1,
 };
 
+struct t25_result_msg {
+	u8 status;
+	u8 info[5];
+} __packed;
+
 /* Config update context */
 struct mxt_cfg {
 	u8 *raw;
@@ -328,6 +340,8 @@  struct mxt_data {
 	u8 T9_reportid_max;
 	u16 T18_address;
 	u8 T19_reportid;
+	u16 T25_address;
+	u8 T25_reportid;
 	u16 T44_address;
 	u8 T100_reportid_min;
 	u8 T100_reportid_max;
@@ -341,6 +355,9 @@  struct mxt_data {
 	/* for config update handling */
 	struct completion crc_completion;
 
+	/* store selftest result */
+	struct t25_result_msg t25_result;
+
 	u32 *t19_keymap;
 	unsigned int t19_num_keys;
 
@@ -999,6 +1016,41 @@  static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
 	data->update_input = true;
 }
 
+static void mxt_proc_t25_message(struct mxt_data *data, u8 *msg)
+{
+	struct device *dev = &data->client->dev;
+
+	memcpy(&data->t25_result, &msg[1], sizeof(data->t25_result));
+
+	if (data->t25_result.status == MXT_T25_RESULT_STATUS_SUCCESS)
+		return;
+
+	switch (data->t25_result.status) {
+	case MXT_T25_RESULT_STATUS_INVALID:
+		dev_err(dev, "Invalid test code\n");
+		break;
+	case MXT_T25_RESULT_STATUS_NOAVDD:
+		dev_err(dev, "AVdd is not present\n");
+		break;
+	case MXT_T25_RESULT_STATUS_PINFAULT:
+	case MXT_T25_RESULT_STATUS_PINFAULT2:
+		dev_err(dev, "Pin fault (seqnum=%02x)\n",
+			data->t25_result.info[0]);
+		break;
+	case MXT_T25_RESULT_STATUS_SIGFAULT:
+		dev_err(dev, "Signal limit fault\n");
+		break;
+	default:
+		dev_err(dev, "Status %02x\n", data->t25_result.status);
+		break;
+	}
+
+	dev_err(dev, "info: %02x %02x %02x %02x %02x\n",
+		data->t25_result.info[1], data->t25_result.info[2],
+		data->t25_result.info[3], data->t25_result.info[4],
+		data->t25_result.info[5]);
+}
+
 static int mxt_proc_message(struct mxt_data *data, u8 *message)
 {
 	u8 report_id = message[0];
@@ -1023,6 +1075,8 @@  static int mxt_proc_message(struct mxt_data *data, u8 *message)
 	} else if (report_id == data->T19_reportid) {
 		mxt_input_button(data, message);
 		data->update_input = true;
+	} else if (report_id == data->T25_reportid) {
+		mxt_proc_t25_message(data, message);
 	} else {
 		mxt_dump_message(data, message);
 	}
@@ -1231,6 +1285,36 @@  static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
 	return 0;
 }
 
+static int mxt_t25_command(struct mxt_data *data, u8 test_code)
+{
+	int timeout_counter = 0;
+	u16 reg;
+	int ret;
+	u8  val[2];
+
+	reg = data->T25_address;
+	val[0] = 0x3;
+	val[1] = test_code;
+
+	ret = __mxt_write_reg(data->client, reg, sizeof(val), val);
+	if (ret)
+		return ret;
+
+	do {
+		msleep(100);
+		ret = __mxt_read_reg(data->client, reg, sizeof(val), &val);
+		if (ret)
+			return ret;
+	} while ((val[1] != 0) && (timeout_counter++ <= 20));
+
+	if (timeout_counter > 20) {
+		dev_err(&data->client->dev, "Command failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
 static int mxt_acquire_irq(struct mxt_data *data)
 {
 	int error;
@@ -1691,6 +1775,8 @@  static void mxt_free_object_table(struct mxt_data *data)
 	data->T9_reportid_max = 0;
 	data->T18_address = 0;
 	data->T19_reportid = 0;
+	data->T25_address = 0;
+	data->T25_reportid = 0;
 	data->T44_address = 0;
 	data->T100_reportid_min = 0;
 	data->T100_reportid_max = 0;
@@ -1767,6 +1853,10 @@  static int mxt_parse_object_table(struct mxt_data *data,
 		case MXT_SPT_COMMSCONFIG_T18:
 			data->T18_address = object->start_address;
 			break;
+		case MXT_SPT_SELFTEST_T25:
+			data->T25_address = object->start_address;
+			data->T25_reportid = min_id;
+			break;
 		case MXT_SPT_MESSAGECOUNT_T44:
 			data->T44_address = object->start_address;
 			break;
@@ -3002,16 +3092,59 @@  static ssize_t mxt_update_fw_store(struct device *dev,
 	return count;
 }
 
+static ssize_t mxt_selftest_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+
+	if (!data->T25_address)
+		return -EOPNOTSUPP;
+
+	if (!data->t25_result.status)
+		return -ENODATA;
+
+	if (data->t25_result.status == MXT_T25_RESULT_STATUS_SUCCESS)
+		return scnprintf(buf, PAGE_SIZE, "PASS\n");
+
+	return scnprintf(buf, PAGE_SIZE, "FAIL\n");
+}
+
+static ssize_t mxt_selftest_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	u8 test_code;
+	int ret;
+
+	if (!data->T25_address)
+		return -EOPNOTSUPP;
+
+	/* Refer to documentation for available seltest codes */
+	if (kstrtou8(buf, 0, &test_code))
+		return -EINVAL;
+
+	data->t25_result.status = 0;
+
+	ret = mxt_t25_command(data, test_code);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
 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);
 static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+static DEVICE_ATTR(selftest, S_IWUSR | S_IRUGO, mxt_selftest_show, mxt_selftest_store);
 
 static struct attribute *mxt_attrs[] = {
 	&dev_attr_fw_version.attr,
 	&dev_attr_hw_version.attr,
 	&dev_attr_object.attr,
 	&dev_attr_update_fw.attr,
+	&dev_attr_selftest.attr,
 	NULL
 };