@@ -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
};
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(+)