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
 };
 
