diff --git a/Documentation/ABI/testing/sysfs-driver-input-exc3000 b/Documentation/ABI/testing/sysfs-driver-input-exc3000
new file mode 100644
index 000000000000..3d316d54f81c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-input-exc3000
@@ -0,0 +1,15 @@
+What:		/sys/bus/i2c/devices/xxx/fw_version
+Date:		Aug 2020
+Contact:	linux-input@vger.kernel.org
+Description:    Reports the firmware version provided by the touchscreen, for example "00_T6" on a EXC80H60
+
+		Access: Read
+		Valid values: Represented as string
+
+What:		/sys/bus/i2c/devices/xxx/model
+Date:		Aug 2020
+Contact:	linux-input@vger.kernel.org
+Description:    Reports the model identification provided by the touchscreen, for example "Orion_1320" on a EXC80H60
+
+		Access: Read
+		Valid values: Represented as string
diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c
index 203f50acaab7..a6597f026980 100644
--- a/drivers/input/touchscreen/exc3000.c
+++ b/drivers/input/touchscreen/exc3000.c
@@ -27,6 +27,9 @@
 #define EXC3000_LEN_FRAME		66
 #define EXC3000_LEN_POINT		10
 
+#define EXC3000_LEN_MODEL_NAME		16
+#define EXC3000_LEN_FW_VERSION		16
+
 #define EXC3000_MT1_EVENT		0x06
 #define EXC3000_MT2_EVENT		0x18
 
@@ -71,6 +74,11 @@ struct exc3000_data {
 	struct gpio_desc *reset;
 	struct timer_list timer;
 	u8 buf[2 * EXC3000_LEN_FRAME];
+	struct completion wait_event;
+	struct mutex query_lock;
+	int query_result;
+	char model[EXC3000_LEN_MODEL_NAME];
+	char fw_version[EXC3000_LEN_FW_VERSION];
 };
 
 static void exc3000_report_slots(struct input_dev *input,
@@ -156,6 +164,28 @@ static int exc3000_read_data(struct exc3000_data *data,
 	return 0;
 }
 
+static int exc3000_query_interrupt(struct exc3000_data *data)
+{
+	u8 *buf = data->buf;
+	int error;
+
+	error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
+	if (error < 0)
+		return error;
+
+	if (buf[0] != 'B')
+		return -EPROTO;
+
+	if (buf[4] == 'E')
+		strlcpy(data->model, buf + 5, sizeof(data->model));
+	else if (buf[4] == 'D')
+		strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version));
+	else
+		return -EPROTO;
+
+	return 0;
+}
+
 static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 {
 	struct exc3000_data *data = dev_id;
@@ -164,6 +194,12 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 	int slots, total_slots;
 	int error;
 
+	if (mutex_is_locked(&data->query_lock)) {
+		data->query_result = exc3000_query_interrupt(data);
+		complete(&data->wait_event);
+		goto out;
+	}
+
 	error = exc3000_read_data(data, buf, &total_slots);
 	if (error) {
 		/* Schedule a timer to release "stuck" contacts */
@@ -191,11 +227,91 @@ out:
 	return IRQ_HANDLED;
 }
 
+static ssize_t fw_version_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct exc3000_data *data = i2c_get_clientdata(client);
+	static const u8 request[68] = {
+		0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00
+	};
+	int error;
+
+	mutex_lock(&data->query_lock);
+
+	data->query_result = -ETIMEDOUT;
+	reinit_completion(&data->wait_event);
+
+	error = i2c_master_send(client, request, sizeof(request));
+	if (error < 0) {
+		mutex_unlock(&data->query_lock);
+		return error;
+	}
+
+	wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
+	mutex_unlock(&data->query_lock);
+
+	if (data->query_result < 0)
+		return data->query_result;
+
+	return sprintf(buf, "%s\n", data->fw_version);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static ssize_t exc3000_get_model(struct exc3000_data *data)
+{
+	static const u8 request[68] = {
+		0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00
+	};
+	struct i2c_client *client = data->client;
+	int error;
+
+	mutex_lock(&data->query_lock);
+	data->query_result = -ETIMEDOUT;
+	reinit_completion(&data->wait_event);
+
+	error = i2c_master_send(client, request, sizeof(request));
+	if (error < 0) {
+		mutex_unlock(&data->query_lock);
+		return error;
+	}
+
+	wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
+	mutex_unlock(&data->query_lock);
+
+	return data->query_result;
+}
+
+static ssize_t model_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct exc3000_data *data = i2c_get_clientdata(client);
+	int error;
+
+	error = exc3000_get_model(data);
+	if (error < 0)
+		return error;
+
+	return sprintf(buf, "%s\n", data->model);
+}
+static DEVICE_ATTR_RO(model);
+
+static struct attribute *sysfs_attrs[] = {
+	&dev_attr_fw_version.attr,
+	&dev_attr_model.attr,
+	NULL
+};
+
+static struct attribute_group exc3000_attribute_group = {
+	.attrs = sysfs_attrs
+};
+
 static int exc3000_probe(struct i2c_client *client)
 {
 	struct exc3000_data *data;
 	struct input_dev *input;
-	int error, max_xy;
+	int error, max_xy, retry;
 
 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -209,6 +325,8 @@ static int exc3000_probe(struct i2c_client *client)
 		data->info = &exc3000_info[eeti_dev_id];
 	}
 	timer_setup(&data->timer, exc3000_timer, 0);
+	init_completion(&data->wait_event);
+	mutex_init(&data->query_lock);
 
 	data->reset = devm_gpiod_get_optional(&client->dev, "reset",
 					      GPIOD_OUT_HIGH);
@@ -226,6 +344,7 @@ static int exc3000_probe(struct i2c_client *client)
 		return -ENOMEM;
 
 	data->input = input;
+	input_set_drvdata(input, data);
 
 	input->name = data->info->name;
 	input->id.bustype = BUS_I2C;
@@ -251,6 +370,33 @@ static int exc3000_probe(struct i2c_client *client)
 	if (error)
 		return error;
 
+	/*
+	 * I²C does not have built-in recovery, so retry on failure. This
+	 * ensures, that the device probe will not fail for temporary issues
+	 * on the bus.  This is not needed for the sysfs calls (userspace
+	 * will receive the error code and can start another query) and
+	 * cannot be done for touch events (but that only means loosing one
+	 * or two touch events anyways).
+	 */
+	for (retry = 0; retry < 3; retry++) {
+		error = exc3000_get_model(data);
+		if (!error)
+			break;
+		dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n",
+			 retry + 1, error);
+	}
+
+	if (error)
+		return error;
+
+	dev_dbg(&client->dev, "TS Model: %s", data->model);
+
+	i2c_set_clientdata(client, data);
+
+	error = devm_device_add_group(&client->dev, &exc3000_attribute_group);
+	if (error)
+		return error;
+
 	return 0;
 }