Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 3/7] dt-bindings: input: touchscreen: sx8654: add compatible models
From: Richard Leitner @ 2018-10-16  9:16 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland
  Cc: linux-input, devicetree, linux-kernel, Richard Leitner
In-Reply-To: <20181016091653.26896-1-richard.leitner@skidata.com>

As the sx865[456] share the same datasheet and differ only in the
presence of a "capacitive proximity detection circuit" and a "haptics
motor driver for LRA/ERM" add them to the compatbiles. As the driver
doesn't implement these features it should be no problem.

Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
 Documentation/devicetree/bindings/input/touchscreen/sx8654.txt | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
index ca521d8f7d65..a538678424dd 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
@@ -1,7 +1,10 @@
 * Semtech SX8654 I2C Touchscreen Controller
 
 Required properties:
-- compatible: must be "semtech,sx8654"
+- compatible: must be one of the following, depending on the model:
+	"semtech,sx8654"
+	"semtech,sx8655"
+	"semtech,sx8656"
 - reg: i2c slave address
 - interrupts: touch controller interrupt
 
-- 
2.11.0

^ permalink raw reply related

* [PATCH 4/7] Input: sx8654 - add sx8655 and sx8656 to compatibles
From: Richard Leitner @ 2018-10-16  9:16 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland
  Cc: linux-input, devicetree, linux-kernel, Richard Leitner
In-Reply-To: <20181016091653.26896-1-richard.leitner@skidata.com>

As the sx865[456] share the same datasheet and differ only in the
presence of a "capacitive proximity detection circuit" and a "haptics
motor driver for LRA/ERM" add them to the compatbiles. As the driver
doesn't implement these features it should be no problem.

Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
 drivers/input/touchscreen/sx8654.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index 059127490c8d..622283b49d7c 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -222,6 +222,8 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
 
 static const struct of_device_id sx8654_of_match[] = {
 	{ .compatible = "semtech,sx8654", },
+	{ .compatible = "semtech,sx8655", },
+	{ .compatible = "semtech,sx8656", },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, sx8654_of_match);
@@ -326,6 +328,8 @@ static int sx8654_probe(struct i2c_client *client,
 
 static const struct i2c_device_id sx8654_id_table[] = {
 	{ "semtech_sx8654", 0 },
+	{ "semtech_sx8655", 0 },
+	{ "semtech_sx8656", 0 },
 	{ },
 };
 MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
-- 
2.11.0

^ permalink raw reply related

* [PATCH 5/7] dt-bindings: input: touchscreen: sx8654: add sx8650 to comatibles
From: Richard Leitner @ 2018-10-16  9:22 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland
  Cc: linux-input, devicetree, linux-kernel, Richard Leitner
In-Reply-To: <20181016091653.26896-1-richard.leitner@skidata.com>

As the sx8650 is quite similar to the sx8654 support for it will be
added in the driver. Therefore add it to the compatibles.

Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
 Documentation/devicetree/bindings/input/touchscreen/sx8654.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
index a538678424dd..0ebe6dd043c7 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
@@ -2,6 +2,7 @@
 
 Required properties:
 - compatible: must be one of the following, depending on the model:
+	"semtech,sx8650"
 	"semtech,sx8654"
 	"semtech,sx8655"
 	"semtech,sx8656"
-- 
2.11.0

^ permalink raw reply related

* [PATCH 6/7] Input: sx8654 - add sx8650 support
From: Richard Leitner @ 2018-10-16  9:22 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland
  Cc: linux-input, devicetree, linux-kernel, Richard Leitner
In-Reply-To: <20181016091653.26896-1-richard.leitner@skidata.com>

The sx8654 and sx8650 are quite similar, therefore add support for the
sx8650 within the sx8654 driver.

Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
 drivers/input/touchscreen/sx8654.c | 186 ++++++++++++++++++++++++++++++++-----
 1 file changed, 163 insertions(+), 23 deletions(-)

diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index 622283b49d7c..33f7a0be4ef8 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -29,12 +29,13 @@
 
 #include <linux/input.h>
 #include <linux/module.h>
-#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/gpio/consumer.h>
 #include <linux/delay.h>
+#include <linux/hrtimer.h>
 
 /* register addresses */
 #define I2C_REG_TOUCH0			0x00
@@ -44,9 +45,11 @@
 #define I2C_REG_IRQSRC			0x23
 #define I2C_REG_SOFTRESET		0x3f
 
+#define I2C_REG_SX8650_STAT		0x05
+#define SX8650_STAT_CONVIRQ		BIT(7)
+
 /* commands */
 #define CMD_READ_REGISTER		0x40
-#define CMD_MANUAL			0xc0
 #define CMD_PENTRG			0xe0
 
 /* value for I2C_REG_SOFTRESET */
@@ -58,11 +61,12 @@
 
 /* bits for RegTouch1 */
 #define CONDIRQ				0x20
+#define RPDNT_100K			0x00
 #define FILT_7SA			0x03
 
 /* bits for I2C_REG_CHANMASK */
-#define CONV_X				0x80
-#define CONV_Y				0x40
+#define CONV_X				BIT(7)
+#define CONV_Y				BIT(6)
 
 /* coordinates rate: higher nibble of CTRL0 register */
 #define RATE_MANUAL			0x00
@@ -71,14 +75,110 @@
 /* power delay: lower nibble of CTRL0 register */
 #define POWDLY_1_1MS			0x0b
 
+/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
+ * last PENIRQ after which we assume the pen is lifted.
+ */
+#define SX8650_PENIRQ_TIMEOUT		(80 * 1100 * 1000)
+
 #define MAX_12BIT			((1 << 12) - 1)
+#define MAX_I2C_READ_LEN		10 /* see datasheet section 5.1.5 */
+
+/* channel definition */
+#define CH_X				0x00
+#define CH_Y				0x01
+
+struct sx865x_data {
+	u8 cmd_manual;
+	u8 chan_mask;
+	u8 has_irq_penrelease;
+	u8 has_reg_irqmask;
+	irq_handler_t irqh;
+};
 
 struct sx8654 {
 	struct input_dev *input;
 	struct i2c_client *client;
 	struct gpio_desc *gpio_reset;
+	struct hrtimer timer;
+
+	const struct sx865x_data *data;
 };
 
+static enum hrtimer_restart sx865x_penrelease_timer_handler(struct hrtimer *h)
+{
+	struct sx8654 *ts = container_of(h, struct sx8654, timer);
+
+	input_report_key(ts->input, BTN_TOUCH, 0);
+	input_sync(ts->input);
+	dev_dbg(&ts->client->dev, "penrelease by timer\n");
+
+	return HRTIMER_NORESTART;
+}
+
+static irqreturn_t sx8650_irq(int irq, void *handle)
+{
+	struct sx8654 *ts = handle;
+	struct device *dev = &ts->client->dev;
+	int len, i, ret;
+	u8 stat;
+	u16 x, y;
+	u8 data[MAX_I2C_READ_LEN];
+	u16 ch;
+	u16 chdata;
+	u8 readlen = hweight32(ts->data->chan_mask) * 2;
+
+	stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
+						    | I2C_REG_SX8650_STAT);
+
+	if (!(stat & SX8650_STAT_CONVIRQ)) {
+		dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
+		return IRQ_HANDLED;
+	}
+
+	len = i2c_master_recv(ts->client, data, readlen);
+	if (len != readlen) {
+		dev_dbg(dev, "ignore short recv (%d)\n", len);
+		return IRQ_HANDLED;
+	}
+
+	ret = hrtimer_try_to_cancel(&ts->timer);
+
+	x = 0;
+	y = 0;
+	for (i = 0; (i + 1) < len; i++) {
+		chdata = data[i] << 8;
+		i++;
+		chdata += data[i];
+
+		if (unlikely(chdata == 0xFFFF)) {
+			dev_dbg(dev, "invalid qualified data @ %d\n", i);
+			continue;
+		} else if (unlikely(chdata & 0x8000)) {
+			dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
+			continue;
+		}
+
+		ch = chdata >> 12;
+		if      (ch == CH_X)
+			x = chdata & MAX_12BIT;
+		else if (ch == CH_Y)
+			y = chdata & MAX_12BIT;
+		else
+			dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
+				 chdata);
+	}
+
+	input_report_abs(ts->input, ABS_X, x);
+	input_report_abs(ts->input, ABS_Y, y);
+	input_report_key(ts->input, BTN_TOUCH, 1);
+	input_sync(ts->input);
+	dev_dbg(dev, "point(%4d,%4d)\n", x, y);
+
+	hrtimer_start(&ts->timer, ktime_set(0, SX8650_PENIRQ_TIMEOUT),
+		      HRTIMER_MODE_REL);
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t sx8654_irq(int irq, void *handle)
 {
 	struct sx8654 *sx8654 = handle;
@@ -127,6 +227,22 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
 	return IRQ_HANDLED;
 }
 
+static const struct sx865x_data sx8650_data = {
+	.cmd_manual = 0xb0,
+	.has_irq_penrelease = 0,
+	.has_reg_irqmask = 0,
+	.chan_mask = (CONV_X | CONV_Y),
+	.irqh = sx8650_irq,
+};
+
+static const struct sx865x_data sx8654_data = {
+	.cmd_manual = 0xc0,
+	.has_irq_penrelease = 1,
+	.has_reg_irqmask = 1,
+	.chan_mask = (CONV_X | CONV_Y),
+	.irqh = sx8654_irq,
+};
+
 static int sx8654_reset(struct sx8654 *ts)
 {
 	int err;
@@ -182,13 +298,13 @@ static void sx8654_close(struct input_dev *dev)
 	disable_irq(client->irq);
 
 	/* enable manual mode mode */
-	error = i2c_smbus_write_byte(client, CMD_MANUAL);
+	error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
 	if (error) {
 		dev_err(&client->dev, "writing command CMD_MANUAL failed");
 		return;
 	}
 
-	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
 	if (error) {
 		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
 		return;
@@ -196,10 +312,30 @@ static void sx8654_close(struct input_dev *dev)
 }
 
 #ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+	{
+		.compatible = "semtech,sx8650",
+		.data = &sx8650_data,
+	}, {
+		.compatible = "semtech,sx8654",
+		.data = &sx8654_data,
+	}, {
+		.compatible = "semtech,sx8655",
+		.data = &sx8654_data,
+	}, {
+		.compatible = "semtech,sx8656",
+		.data = &sx8654_data,
+	}, {},
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+
 static int sx8654_get_ofdata(struct sx8654 *ts)
 {
 	struct device *dev = &ts->client->dev;
 	struct device_node *node = dev->of_node;
+	const struct of_device_id *of_id = of_match_device(sx8654_of_match,
+							   dev);
+	const struct sx865x_data *data = (struct sx865x_data *)of_id->data;
 	int err;
 
 	if (!node) {
@@ -207,6 +343,8 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
 		return 0;
 	}
 
+	ts->data = data;
+
 	ts->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 	if (PTR_ERR(ts->gpio_reset) == -EPROBE_DEFER) {
 		return -EPROBE_DEFER;
@@ -219,18 +357,11 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
 
 	return 0;
 }
-
-static const struct of_device_id sx8654_of_match[] = {
-	{ .compatible = "semtech,sx8654", },
-	{ .compatible = "semtech,sx8655", },
-	{ .compatible = "semtech,sx8656", },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, sx8654_of_match);
 #else /* CONFIG_OF */
 static int sx8654_get_ofdata(struct sx8654 *ts)
 {
 	ts->gpio_reset = NULL;
+	ts->data = &sx8654_data;
 	return 0;
 }
 #endif /* CONFIG_OF */
@@ -258,6 +389,12 @@ static int sx8654_probe(struct i2c_client *client,
 		return error;
 	}
 
+	if (!sx8654->data->has_irq_penrelease) {
+		dev_dbg(&client->dev, "use timer for penrelease\n");
+		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		sx8654->timer.function = sx865x_penrelease_timer_handler;
+	}
+
 	input = devm_input_allocate_device(&client->dev);
 	if (!input)
 		return -ENOMEM;
@@ -284,29 +421,31 @@ static int sx8654_probe(struct i2c_client *client,
 	}
 
 	error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
-					  CONV_X | CONV_Y);
+					  sx8654->data->chan_mask);
 	if (error) {
 		dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
 		return error;
 	}
 
-	error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
-					  IRQ_PENTOUCH_TOUCHCONVDONE |
-						IRQ_PENRELEASE);
-	if (error) {
-		dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
-		return error;
+	if (sx8654->data->has_reg_irqmask) {
+		error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+						  IRQ_PENTOUCH_TOUCHCONVDONE |
+							IRQ_PENRELEASE);
+		if (error) {
+			dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
+			return error;
+		}
 	}
 
 	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
-					  CONDIRQ | FILT_7SA);
+					  CONDIRQ | RPDNT_100K | FILT_7SA);
 	if (error) {
 		dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
 		return error;
 	}
 
 	error = devm_request_threaded_irq(&client->dev, client->irq,
-					  NULL, sx8654_irq,
+					  NULL, sx8654->data->irqh,
 					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 					  client->name, sx8654);
 	if (error) {
@@ -327,6 +466,7 @@ static int sx8654_probe(struct i2c_client *client,
 }
 
 static const struct i2c_device_id sx8654_id_table[] = {
+	{ "semtech_sx8650", 0 },
 	{ "semtech_sx8654", 0 },
 	{ "semtech_sx8655", 0 },
 	{ "semtech_sx8656", 0 },
-- 
2.11.0

^ permalink raw reply related

* [PATCH 7/7] Input: sx8654 - use common of_touchscreen functions
From: Richard Leitner @ 2018-10-16  9:23 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland
  Cc: linux-input, devicetree, linux-kernel, Richard Leitner
In-Reply-To: <20181016091653.26896-1-richard.leitner@skidata.com>

of_touchscreen.c provides a common interface for a axis invertion and
swapping of touchscreens. Therefore use it in the sx8654 driver.

Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
 drivers/input/touchscreen/sx8654.c | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index 33f7a0be4ef8..08b064f96b4e 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -36,6 +36,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/delay.h>
 #include <linux/hrtimer.h>
+#include <linux/input/touchscreen.h>
 
 /* register addresses */
 #define I2C_REG_TOUCH0			0x00
@@ -100,6 +101,7 @@ struct sx8654 {
 	struct i2c_client *client;
 	struct gpio_desc *gpio_reset;
 	struct hrtimer timer;
+	struct touchscreen_properties props;
 
 	const struct sx865x_data *data;
 };
@@ -168,8 +170,7 @@ static irqreturn_t sx8650_irq(int irq, void *handle)
 				 chdata);
 	}
 
-	input_report_abs(ts->input, ABS_X, x);
-	input_report_abs(ts->input, ABS_Y, y);
+	touchscreen_report_pos(ts->input, &ts->props, x, y, false);
 	input_report_key(ts->input, BTN_TOUCH, 1);
 	input_sync(ts->input);
 	dev_dbg(dev, "point(%4d,%4d)\n", x, y);
@@ -215,8 +216,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
 		x = ((data[0] & 0xf) << 8) | (data[1]);
 		y = ((data[2] & 0xf) << 8) | (data[3]);
 
-		input_report_abs(sx8654->input, ABS_X, x);
-		input_report_abs(sx8654->input, ABS_Y, y);
+		touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
+				       false);
 		input_report_key(sx8654->input, BTN_TOUCH, 1);
 		input_sync(sx8654->input);
 
@@ -355,6 +356,8 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
 	}
 	dev_dbg(dev, "got GPIO reset pin\n");
 
+	touchscreen_parse_properties(ts->input, false, &ts->props);
+
 	return 0;
 }
 #else /* CONFIG_OF */
@@ -380,25 +383,11 @@ static int sx8654_probe(struct i2c_client *client,
 	sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
 	if (!sx8654)
 		return -ENOMEM;
-
 	sx8654->client = client;
 
-	error = sx8654_get_ofdata(sx8654);
-	if (error) {
-		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
-		return error;
-	}
-
-	if (!sx8654->data->has_irq_penrelease) {
-		dev_dbg(&client->dev, "use timer for penrelease\n");
-		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-		sx8654->timer.function = sx865x_penrelease_timer_handler;
-	}
-
 	input = devm_input_allocate_device(&client->dev);
 	if (!input)
 		return -ENOMEM;
-
 	sx8654->input = input;
 
 	input->name = "SX8654 I2C Touchscreen";
@@ -414,6 +403,18 @@ static int sx8654_probe(struct i2c_client *client,
 
 	input_set_drvdata(sx8654->input, sx8654);
 
+	error = sx8654_get_ofdata(sx8654);
+	if (error) {
+		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
+		return error;
+	}
+
+	if (!sx8654->data->has_irq_penrelease) {
+		dev_dbg(&client->dev, "use timer for penrelease\n");
+		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		sx8654->timer.function = sx865x_penrelease_timer_handler;
+	}
+
 	error = sx8654_reset(sx8654);
 	if (error) {
 		dev_err(&client->dev, "reset failed");
-- 
2.11.0

^ permalink raw reply related

* Re: [PATCH v2 0/8] OLPC 1.75 Keyboard/Touchpad fixes
From: Lubomir Rintel @ 2018-10-16  9:44 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Dmitry Torokhov, Michael Turquette, Rob Herring, Mark Rutland,
	Stephen Boyd, James Cameron, linux-input, devicetree,
	linux-kernel, linux-clk
In-Reply-To: <20181015195612.GB29649@amd>

Hello Pavel,

On Mon, 2018-10-15 at 21:56 +0200, Pavel Machek wrote:
> Hi!
> 
> > This makes keyboard/touchpad work on a DT MMP2 platform.
> > 
> > I believe that it would be a good idea if this, once reviewed, went
> > in
> > via the input tree. The DT and CLK parts got reviews/acks.
> > 
> > Changes from v1:
> > - Basically none, just re-send, including Ack and Review tags in
> > patches
> >   that received them. Plus an extra Cc.
> >   I'd be very thankful for reviews.
> 
> Ok, I believe I have OLPC 1.75, and it is actually a nice
> machine. Can I get recent kernel working there in useful fashion? (Is
> there docs somewhere)?

A lovely machine indeed. I've written this now:

https://github.com/hackerspace/olpc-xo175-linux/wiki/How-to-run-an-up-to-date-Linux-on-a-XO-1.75

I didn't test it yet -- will do when I get home in the evening. But
chances are it's good enough and I guess you'd be able to get it
working even if I messed up some details.

Please let me know if it works for you -- I'm having trouble getting
reviews/acks/tested-bys for the XO-1.75 patch sets [1] and will be
super thankful if I get some feedback from you.

[1] https://lore.kernel.org/patchwork/bundle/lkundrak/OLPC%20XO%201.75/

> 
> Thanks,
> 									
> Pavel

Cheers,
Lubo

^ permalink raw reply

* [PATCH] Input: uinput - fix Spectre v1 vulnerability
From: Gustavo A. R. Silva @ 2018-10-16 11:13 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Gustavo A. R. Silva

setup.code can be indirectly controlled by user-space, hence leading to
a potential exploitation of the Spectre variant 1 vulnerability.

This issue was detected with the help of Smatch:

drivers/input/misc/uinput.c:512 uinput_abs_setup() warn: potential
spectre issue 'dev->absinfo' [w] (local cap)

Fix this by sanitizing setup.code before using it to index dev->absinfo.

Notice that given that speculation windows are large, the policy is
to kill the speculation on the first load and not worry if it can be
completed with a dependent load/store [1].

[1] https://marc.info/?l=linux-kernel&m=152449131114778&w=2

Cc: stable@vger.kernel.org
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
 drivers/input/misc/uinput.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 8ec483e..97b0809 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -42,6 +42,8 @@
 #include <linux/input/mt.h>
 #include "../input-compat.h"
 
+#include <linux/nospec.h>
+
 #define UINPUT_NAME		"uinput"
 #define UINPUT_BUFFER_SIZE	16
 #define UINPUT_NUM_REQUESTS	16
@@ -497,6 +499,7 @@ static int uinput_abs_setup(struct uinput_device *udev,
 
 	if (setup.code > ABS_MAX)
 		return -ERANGE;
+	setup.code = array_index_nospec(setup.code, ABS_MAX + 1);
 
 	dev = udev->dev;
 
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH] Input: uinput - fix Spectre v1 vulnerability
From: Dmitry Torokhov @ 2018-10-16 17:21 UTC (permalink / raw)
  To: Gustavo A. R. Silva; +Cc: linux-input, linux-kernel
In-Reply-To: <20181016111313.GA28307@embeddedor.com>

Hi Gustavo,

On Tue, Oct 16, 2018 at 01:13:13PM +0200, Gustavo A. R. Silva wrote:
> setup.code can be indirectly controlled by user-space, hence leading to
> a potential exploitation of the Spectre variant 1 vulnerability.
> 
> This issue was detected with the help of Smatch:
> 
> drivers/input/misc/uinput.c:512 uinput_abs_setup() warn: potential
> spectre issue 'dev->absinfo' [w] (local cap)
> 
> Fix this by sanitizing setup.code before using it to index dev->absinfo.

So we are saying that attacker, by repeatedly calling ioctl(...,
UI_ABS_SETUP, ...) will be able to poison branch predictor and discover
another program or kernel secrets? But uinput is a privileged interface
open to root only, as it allows injecting arbitrary keystrokes into the
kernel. And since only root can use uinput, meh?

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 2/7] Input: sx8654 - add reset-gpio support
From: Dmitry Torokhov @ 2018-10-16 17:39 UTC (permalink / raw)
  To: Richard Leitner
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel
In-Reply-To: <20181016091653.26896-3-richard.leitner@skidata.com>

Hi Richard,

On Tue, Oct 16, 2018 at 11:16:48AM +0200, Richard Leitner wrote:
> The sx8654 features a NRST input which may be connected to a GPIO.
> Therefore add support for hard-resetting the sx8654 via this NRST.
> 
> If the reset-gpio property is provided the sx8654 is resetted via NRST
> instead of the soft-reset via I2C.
> 
> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
> ---
>  drivers/input/touchscreen/sx8654.c | 88 ++++++++++++++++++++++++++++++++------
>  1 file changed, 74 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
> index ed29db3ec731..059127490c8d 100644
> --- a/drivers/input/touchscreen/sx8654.c
> +++ b/drivers/input/touchscreen/sx8654.c
> @@ -33,6 +33,8 @@
>  #include <linux/i2c.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/delay.h>
>  
>  /* register addresses */
>  #define I2C_REG_TOUCH0			0x00
> @@ -74,6 +76,7 @@
>  struct sx8654 {
>  	struct input_dev *input;
>  	struct i2c_client *client;
> +	struct gpio_desc *gpio_reset;
>  };
>  
>  static irqreturn_t sx8654_irq(int irq, void *handle)
> @@ -124,6 +127,27 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
>  	return IRQ_HANDLED;
>  }
>  
> +static int sx8654_reset(struct sx8654 *ts)
> +{
> +	int err;
> +
> +	if (ts->gpio_reset) {
> +		gpiod_set_value_cansleep(ts->gpio_reset, 1);
> +		udelay(2); /* Tpulse > 1µs */
> +		gpiod_set_value_cansleep(ts->gpio_reset, 0);
> +
> +		return 0;
> +	}
> +
> +	dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
> +	err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
> +					SOFTRESET_VALUE);
> +	if (err)
> +		return err;

Have this in an "else" branch?

> +
> +	return 0;
> +}
> +
>  static int sx8654_open(struct input_dev *dev)
>  {
>  	struct sx8654 *sx8654 = input_get_drvdata(dev);
> @@ -171,6 +195,44 @@ static void sx8654_close(struct input_dev *dev)
>  	}
>  }
>  
> +#ifdef CONFIG_OF
> +static int sx8654_get_ofdata(struct sx8654 *ts)
> +{
> +	struct device *dev = &ts->client->dev;
> +	struct device_node *node = dev->of_node;
> +	int err;
> +
> +	if (!node) {
> +		ts->gpio_reset = NULL;
> +		return 0;
> +	}

There is no need to wrap this into CONFIG_OF or test "node". Just use
devm_gpiod_get_optional() and it will work equally well on DT, ACPI, and
static board systems.

> +
> +	ts->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> +	if (PTR_ERR(ts->gpio_reset) == -EPROBE_DEFER) {
> +		return -EPROBE_DEFER;
> +	} else if (IS_ERR(ts->gpio_reset)) {
> +		err = PTR_ERR(ts->gpio_reset);

I'd do:

		if (err != -EPROBE_DEFER)
			dev_err(...)

here instead of separate branch.

> +		dev_err(dev, "unable to request GPIO reset pin (%d)\n", err);
> +		return err;
> +	}
> +	dev_dbg(dev, "got GPIO reset pin\n");

Also, I'd move all this code directly into probe() since all this
#ifdefery is not needed.

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sx8654_of_match[] = {
> +	{ .compatible = "semtech,sx8654", },
> +	{ },
> +};

Please keep the device table wehere it was.

> +MODULE_DEVICE_TABLE(of, sx8654_of_match);
> +#else /* CONFIG_OF */
> +static int sx8654_get_ofdata(struct sx8654 *ts)
> +{
> +	ts->gpio_reset = NULL;
> +	return 0;
> +}
> +#endif /* CONFIG_OF */
> +
>  static int sx8654_probe(struct i2c_client *client,
>  			const struct i2c_device_id *id)
>  {
> @@ -186,10 +248,20 @@ static int sx8654_probe(struct i2c_client *client,
>  	if (!sx8654)
>  		return -ENOMEM;
>  
> +	sx8654->client = client;
> +
> +	error = sx8654_get_ofdata(sx8654);
> +	if (error) {
> +		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
> +		return error;
> +	}
> +
>  	input = devm_input_allocate_device(&client->dev);
>  	if (!input)
>  		return -ENOMEM;
>  
> +	sx8654->input = input;
> +
>  	input->name = "SX8654 I2C Touchscreen";
>  	input->id.bustype = BUS_I2C;
>  	input->dev.parent = &client->dev;
> @@ -201,15 +273,11 @@ static int sx8654_probe(struct i2c_client *client,
>  	input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
>  	input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
>  
> -	sx8654->client = client;
> -	sx8654->input = input;
> -

Why do we need to move this?

>  	input_set_drvdata(sx8654->input, sx8654);
>  
> -	error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
> -					  SOFTRESET_VALUE);
> +	error = sx8654_reset(sx8654);
>  	if (error) {
> -		dev_err(&client->dev, "writing softreset value failed");
> +		dev_err(&client->dev, "reset failed");
>  		return error;
>  	}
>  
> @@ -256,14 +324,6 @@ static int sx8654_probe(struct i2c_client *client,
>  	return 0;
>  }
>  
> -#ifdef CONFIG_OF
> -static const struct of_device_id sx8654_of_match[] = {
> -	{ .compatible = "semtech,sx8654", },
> -	{ },
> -};
> -MODULE_DEVICE_TABLE(of, sx8654_of_match);
> -#endif
> -
>  static const struct i2c_device_id sx8654_id_table[] = {
>  	{ "semtech_sx8654", 0 },
>  	{ },
> -- 
> 2.11.0
> 

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 6/7] Input: sx8654 - add sx8650 support
From: Dmitry Torokhov @ 2018-10-16 17:48 UTC (permalink / raw)
  To: Richard Leitner
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel
In-Reply-To: <20181016092248.27288-1-richard.leitner@skidata.com>

On Tue, Oct 16, 2018 at 11:22:48AM +0200, Richard Leitner wrote:
> The sx8654 and sx8650 are quite similar, therefore add support for the
> sx8650 within the sx8654 driver.
> 
> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
> ---
>  drivers/input/touchscreen/sx8654.c | 186 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 163 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
> index 622283b49d7c..33f7a0be4ef8 100644
> --- a/drivers/input/touchscreen/sx8654.c
> +++ b/drivers/input/touchscreen/sx8654.c
> @@ -29,12 +29,13 @@
>  
>  #include <linux/input.h>
>  #include <linux/module.h>
> -#include <linux/of.h>
> +#include <linux/of_device.h>
>  #include <linux/i2c.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
>  #include <linux/gpio/consumer.h>
>  #include <linux/delay.h>
> +#include <linux/hrtimer.h>
>  
>  /* register addresses */
>  #define I2C_REG_TOUCH0			0x00
> @@ -44,9 +45,11 @@
>  #define I2C_REG_IRQSRC			0x23
>  #define I2C_REG_SOFTRESET		0x3f
>  
> +#define I2C_REG_SX8650_STAT		0x05
> +#define SX8650_STAT_CONVIRQ		BIT(7)
> +
>  /* commands */
>  #define CMD_READ_REGISTER		0x40
> -#define CMD_MANUAL			0xc0
>  #define CMD_PENTRG			0xe0
>  
>  /* value for I2C_REG_SOFTRESET */
> @@ -58,11 +61,12 @@
>  
>  /* bits for RegTouch1 */
>  #define CONDIRQ				0x20
> +#define RPDNT_100K			0x00
>  #define FILT_7SA			0x03
>  
>  /* bits for I2C_REG_CHANMASK */
> -#define CONV_X				0x80
> -#define CONV_Y				0x40
> +#define CONV_X				BIT(7)
> +#define CONV_Y				BIT(6)

If you are using BIT() you need to include include/linux/bitops.h

I also would prefer conversion to using BIT() as a separate patch.

>  
>  /* coordinates rate: higher nibble of CTRL0 register */
>  #define RATE_MANUAL			0x00
> @@ -71,14 +75,110 @@
>  /* power delay: lower nibble of CTRL0 register */
>  #define POWDLY_1_1MS			0x0b
>  
> +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
> + * last PENIRQ after which we assume the pen is lifted.
> + */
> +#define SX8650_PENIRQ_TIMEOUT		(80 * 1100 * 1000)
> +
>  #define MAX_12BIT			((1 << 12) - 1)
> +#define MAX_I2C_READ_LEN		10 /* see datasheet section 5.1.5 */
> +
> +/* channel definition */
> +#define CH_X				0x00
> +#define CH_Y				0x01
> +
> +struct sx865x_data {
> +	u8 cmd_manual;
> +	u8 chan_mask;
> +	u8 has_irq_penrelease;
> +	u8 has_reg_irqmask;
> +	irq_handler_t irqh;
> +};
>  
>  struct sx8654 {
>  	struct input_dev *input;
>  	struct i2c_client *client;
>  	struct gpio_desc *gpio_reset;
> +	struct hrtimer timer;

Does this have to be hrtimer? Can regular timer be used?

> +
> +	const struct sx865x_data *data;
>  };
>  
> +static enum hrtimer_restart sx865x_penrelease_timer_handler(struct hrtimer *h)
> +{
> +	struct sx8654 *ts = container_of(h, struct sx8654, timer);
> +
> +	input_report_key(ts->input, BTN_TOUCH, 0);
> +	input_sync(ts->input);
> +	dev_dbg(&ts->client->dev, "penrelease by timer\n");
> +
> +	return HRTIMER_NORESTART;
> +}
> +
> +static irqreturn_t sx8650_irq(int irq, void *handle)
> +{
> +	struct sx8654 *ts = handle;
> +	struct device *dev = &ts->client->dev;
> +	int len, i, ret;
> +	u8 stat;
> +	u16 x, y;
> +	u8 data[MAX_I2C_READ_LEN];
> +	u16 ch;
> +	u16 chdata;
> +	u8 readlen = hweight32(ts->data->chan_mask) * 2;
> +
> +	stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
> +						    | I2C_REG_SX8650_STAT);
> +
> +	if (!(stat & SX8650_STAT_CONVIRQ)) {
> +		dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
> +		return IRQ_HANDLED;
> +	}
> +
> +	len = i2c_master_recv(ts->client, data, readlen);
> +	if (len != readlen) {
> +		dev_dbg(dev, "ignore short recv (%d)\n", len);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ret = hrtimer_try_to_cancel(&ts->timer);
> +
> +	x = 0;
> +	y = 0;
> +	for (i = 0; (i + 1) < len; i++) {
> +		chdata = data[i] << 8;
> +		i++;
> +		chdata += data[i];
> +
> +		if (unlikely(chdata == 0xFFFF)) {
> +			dev_dbg(dev, "invalid qualified data @ %d\n", i);
> +			continue;
> +		} else if (unlikely(chdata & 0x8000)) {
> +			dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
> +			continue;
> +		}
> +
> +		ch = chdata >> 12;
> +		if      (ch == CH_X)
> +			x = chdata & MAX_12BIT;
> +		else if (ch == CH_Y)
> +			y = chdata & MAX_12BIT;
> +		else
> +			dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
> +				 chdata);
> +	}
> +
> +	input_report_abs(ts->input, ABS_X, x);
> +	input_report_abs(ts->input, ABS_Y, y);
> +	input_report_key(ts->input, BTN_TOUCH, 1);
> +	input_sync(ts->input);
> +	dev_dbg(dev, "point(%4d,%4d)\n", x, y);
> +
> +	hrtimer_start(&ts->timer, ktime_set(0, SX8650_PENIRQ_TIMEOUT),
> +		      HRTIMER_MODE_REL);
> +	return IRQ_HANDLED;
> +}
> +
>  static irqreturn_t sx8654_irq(int irq, void *handle)
>  {
>  	struct sx8654 *sx8654 = handle;
> @@ -127,6 +227,22 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
>  	return IRQ_HANDLED;
>  }
>  
> +static const struct sx865x_data sx8650_data = {
> +	.cmd_manual = 0xb0,
> +	.has_irq_penrelease = 0,
> +	.has_reg_irqmask = 0,
> +	.chan_mask = (CONV_X | CONV_Y),
> +	.irqh = sx8650_irq,
> +};
> +
> +static const struct sx865x_data sx8654_data = {
> +	.cmd_manual = 0xc0,
> +	.has_irq_penrelease = 1,
> +	.has_reg_irqmask = 1,
> +	.chan_mask = (CONV_X | CONV_Y),
> +	.irqh = sx8654_irq,
> +};
> +
>  static int sx8654_reset(struct sx8654 *ts)
>  {
>  	int err;
> @@ -182,13 +298,13 @@ static void sx8654_close(struct input_dev *dev)
>  	disable_irq(client->irq);
>  
>  	/* enable manual mode mode */
> -	error = i2c_smbus_write_byte(client, CMD_MANUAL);
> +	error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
>  	if (error) {
>  		dev_err(&client->dev, "writing command CMD_MANUAL failed");
>  		return;
>  	}
>  
> -	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
> +	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
>  	if (error) {
>  		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
>  		return;
> @@ -196,10 +312,30 @@ static void sx8654_close(struct input_dev *dev)
>  }
>  
>  #ifdef CONFIG_OF
> +static const struct of_device_id sx8654_of_match[] = {
> +	{
> +		.compatible = "semtech,sx8650",
> +		.data = &sx8650_data,
> +	}, {
> +		.compatible = "semtech,sx8654",
> +		.data = &sx8654_data,
> +	}, {
> +		.compatible = "semtech,sx8655",
> +		.data = &sx8654_data,
> +	}, {
> +		.compatible = "semtech,sx8656",
> +		.data = &sx8654_data,
> +	}, {},
> +};
> +MODULE_DEVICE_TABLE(of, sx8654_of_match);
> +
>  static int sx8654_get_ofdata(struct sx8654 *ts)
>  {
>  	struct device *dev = &ts->client->dev;
>  	struct device_node *node = dev->of_node;
> +	const struct of_device_id *of_id = of_match_device(sx8654_of_match,
> +							   dev);

If you use of_device_get_match_data() you do not need to move device
table around.

> +	const struct sx865x_data *data = (struct sx865x_data *)of_id->data;
>  	int err;
>  
>  	if (!node) {
> @@ -207,6 +343,8 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
>  		return 0;
>  	}
>  
> +	ts->data = data;
> +
>  	ts->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
>  	if (PTR_ERR(ts->gpio_reset) == -EPROBE_DEFER) {
>  		return -EPROBE_DEFER;
> @@ -219,18 +357,11 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
>  
>  	return 0;
>  }
> -
> -static const struct of_device_id sx8654_of_match[] = {
> -	{ .compatible = "semtech,sx8654", },
> -	{ .compatible = "semtech,sx8655", },
> -	{ .compatible = "semtech,sx8656", },
> -	{ },
> -};
> -MODULE_DEVICE_TABLE(of, sx8654_of_match);
>  #else /* CONFIG_OF */
>  static int sx8654_get_ofdata(struct sx8654 *ts)
>  {
>  	ts->gpio_reset = NULL;
> +	ts->data = &sx8654_data;
>  	return 0;
>  }
>  #endif /* CONFIG_OF */
> @@ -258,6 +389,12 @@ static int sx8654_probe(struct i2c_client *client,
>  		return error;
>  	}
>  
> +	if (!sx8654->data->has_irq_penrelease) {
> +		dev_dbg(&client->dev, "use timer for penrelease\n");
> +		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +		sx8654->timer.function = sx865x_penrelease_timer_handler;
> +	}
> +
>  	input = devm_input_allocate_device(&client->dev);
>  	if (!input)
>  		return -ENOMEM;
> @@ -284,29 +421,31 @@ static int sx8654_probe(struct i2c_client *client,
>  	}
>  
>  	error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
> -					  CONV_X | CONV_Y);
> +					  sx8654->data->chan_mask);
>  	if (error) {
>  		dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
>  		return error;
>  	}
>  
> -	error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
> -					  IRQ_PENTOUCH_TOUCHCONVDONE |
> -						IRQ_PENRELEASE);
> -	if (error) {
> -		dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
> -		return error;
> +	if (sx8654->data->has_reg_irqmask) {
> +		error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
> +						  IRQ_PENTOUCH_TOUCHCONVDONE |
> +							IRQ_PENRELEASE);
> +		if (error) {
> +			dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
> +			return error;
> +		}
>  	}
>  
>  	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
> -					  CONDIRQ | FILT_7SA);
> +					  CONDIRQ | RPDNT_100K | FILT_7SA);
>  	if (error) {
>  		dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
>  		return error;
>  	}
>  
>  	error = devm_request_threaded_irq(&client->dev, client->irq,
> -					  NULL, sx8654_irq,
> +					  NULL, sx8654->data->irqh,
>  					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>  					  client->name, sx8654);
>  	if (error) {
> @@ -327,6 +466,7 @@ static int sx8654_probe(struct i2c_client *client,
>  }
>  
>  static const struct i2c_device_id sx8654_id_table[] = {
> +	{ "semtech_sx8650", 0 },

Can we add the data here as well?

>  	{ "semtech_sx8654", 0 },
>  	{ "semtech_sx8655", 0 },
>  	{ "semtech_sx8656", 0 },
> -- 
> 2.11.0
> 

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 7/7] Input: sx8654 - use common of_touchscreen functions
From: Dmitry Torokhov @ 2018-10-16 17:52 UTC (permalink / raw)
  To: Richard Leitner
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel
In-Reply-To: <20181016092310.27346-1-richard.leitner@skidata.com>

On Tue, Oct 16, 2018 at 11:23:10AM +0200, Richard Leitner wrote:
> of_touchscreen.c provides a common interface for a axis invertion and
> swapping of touchscreens. Therefore use it in the sx8654 driver.
> 
> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
> ---
>  drivers/input/touchscreen/sx8654.c | 37 +++++++++++++++++++------------------
>  1 file changed, 19 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
> index 33f7a0be4ef8..08b064f96b4e 100644
> --- a/drivers/input/touchscreen/sx8654.c
> +++ b/drivers/input/touchscreen/sx8654.c
> @@ -36,6 +36,7 @@
>  #include <linux/gpio/consumer.h>
>  #include <linux/delay.h>
>  #include <linux/hrtimer.h>
> +#include <linux/input/touchscreen.h>
>  
>  /* register addresses */
>  #define I2C_REG_TOUCH0			0x00
> @@ -100,6 +101,7 @@ struct sx8654 {
>  	struct i2c_client *client;
>  	struct gpio_desc *gpio_reset;
>  	struct hrtimer timer;
> +	struct touchscreen_properties props;
>  
>  	const struct sx865x_data *data;
>  };
> @@ -168,8 +170,7 @@ static irqreturn_t sx8650_irq(int irq, void *handle)
>  				 chdata);
>  	}
>  
> -	input_report_abs(ts->input, ABS_X, x);
> -	input_report_abs(ts->input, ABS_Y, y);
> +	touchscreen_report_pos(ts->input, &ts->props, x, y, false);
>  	input_report_key(ts->input, BTN_TOUCH, 1);
>  	input_sync(ts->input);
>  	dev_dbg(dev, "point(%4d,%4d)\n", x, y);
> @@ -215,8 +216,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
>  		x = ((data[0] & 0xf) << 8) | (data[1]);
>  		y = ((data[2] & 0xf) << 8) | (data[3]);
>  
> -		input_report_abs(sx8654->input, ABS_X, x);
> -		input_report_abs(sx8654->input, ABS_Y, y);
> +		touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
> +				       false);
>  		input_report_key(sx8654->input, BTN_TOUCH, 1);
>  		input_sync(sx8654->input);
>  
> @@ -355,6 +356,8 @@ static int sx8654_get_ofdata(struct sx8654 *ts)
>  	}
>  	dev_dbg(dev, "got GPIO reset pin\n");
>  

You need to mark axes used by the device before calling
touchscreen_parse_properties(), otherwise you'll get warnings if
corresponding property is present in DT.

> +	touchscreen_parse_properties(ts->input, false, &ts->props);
> +
>  	return 0;
>  }
>  #else /* CONFIG_OF */
> @@ -380,25 +383,11 @@ static int sx8654_probe(struct i2c_client *client,
>  	sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
>  	if (!sx8654)
>  		return -ENOMEM;
> -
>  	sx8654->client = client;
>  
> -	error = sx8654_get_ofdata(sx8654);
> -	if (error) {
> -		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
> -		return error;
> -	}
> -
> -	if (!sx8654->data->has_irq_penrelease) {
> -		dev_dbg(&client->dev, "use timer for penrelease\n");
> -		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> -		sx8654->timer.function = sx865x_penrelease_timer_handler;
> -	}
> -
>  	input = devm_input_allocate_device(&client->dev);
>  	if (!input)
>  		return -ENOMEM;
> -
>  	sx8654->input = input;
>  
>  	input->name = "SX8654 I2C Touchscreen";
> @@ -414,6 +403,18 @@ static int sx8654_probe(struct i2c_client *client,
>  
>  	input_set_drvdata(sx8654->input, sx8654);
>  
> +	error = sx8654_get_ofdata(sx8654);
> +	if (error) {
> +		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
> +		return error;
> +	}
> +
> +	if (!sx8654->data->has_irq_penrelease) {
> +		dev_dbg(&client->dev, "use timer for penrelease\n");
> +		hrtimer_init(&sx8654->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +		sx8654->timer.function = sx865x_penrelease_timer_handler;
> +	}
> +
>  	error = sx8654_reset(sx8654);
>  	if (error) {
>  		dev_err(&client->dev, "reset failed");
> -- 
> 2.11.0
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: uinput - fix Spectre v1 vulnerability
From: Gustavo A. R. Silva @ 2018-10-16 17:52 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20181016172107.GA230131@dtor-ws>

Hi Dmitry,

On 10/16/18 7:21 PM, Dmitry Torokhov wrote:
> Hi Gustavo,
> 
> On Tue, Oct 16, 2018 at 01:13:13PM +0200, Gustavo A. R. Silva wrote:
>> setup.code can be indirectly controlled by user-space, hence leading to
>> a potential exploitation of the Spectre variant 1 vulnerability.
>>
>> This issue was detected with the help of Smatch:
>>
>> drivers/input/misc/uinput.c:512 uinput_abs_setup() warn: potential
>> spectre issue 'dev->absinfo' [w] (local cap)
>>
>> Fix this by sanitizing setup.code before using it to index dev->absinfo.
> 
> So we are saying that attacker, by repeatedly calling ioctl(...,
> UI_ABS_SETUP, ...) will be able to poison branch predictor and discover
> another program or kernel secrets? But uinput is a privileged interface
> open to root only, as it allows injecting arbitrary keystrokes into the
> kernel. And since only root can use uinput, meh?
> 

Oh I see... in that case this is a false positive.

Although, I wonder if all these operations are only accessible to root:

static const struct file_operations uinput_fops = {
        .owner          = THIS_MODULE,
        .open           = uinput_open,
        .release        = uinput_release,
        .read           = uinput_read,
        .write          = uinput_write,
        .poll           = uinput_poll,
        .unlocked_ioctl = uinput_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl   = uinput_compat_ioctl,
#endif
        .llseek         = no_llseek,
};

Thanks for the feedback.
--
Gustavo

^ permalink raw reply

* Re: [PATCH] Input: uinput - fix Spectre v1 vulnerability
From: Dmitry Torokhov @ 2018-10-16 18:09 UTC (permalink / raw)
  To: Gustavo A. R. Silva; +Cc: linux-input, linux-kernel
In-Reply-To: <ead3e95a-1acb-362a-0ee2-d02dbc4f378c@embeddedor.com>

On October 16, 2018 10:52:58 AM PDT, "Gustavo A. R. Silva" <gustavo@embeddedor.com> wrote:
>Hi Dmitry,
>
>On 10/16/18 7:21 PM, Dmitry Torokhov wrote:
>> Hi Gustavo,
>> 
>> On Tue, Oct 16, 2018 at 01:13:13PM +0200, Gustavo A. R. Silva wrote:
>>> setup.code can be indirectly controlled by user-space, hence leading
>to
>>> a potential exploitation of the Spectre variant 1 vulnerability.
>>>
>>> This issue was detected with the help of Smatch:
>>>
>>> drivers/input/misc/uinput.c:512 uinput_abs_setup() warn: potential
>>> spectre issue 'dev->absinfo' [w] (local cap)
>>>
>>> Fix this by sanitizing setup.code before using it to index
>dev->absinfo.
>> 
>> So we are saying that attacker, by repeatedly calling ioctl(...,
>> UI_ABS_SETUP, ...) will be able to poison branch predictor and
>discover
>> another program or kernel secrets? But uinput is a privileged
>interface
>> open to root only, as it allows injecting arbitrary keystrokes into
>the
>> kernel. And since only root can use uinput, meh?
>> 
>
>Oh I see... in that case this is a false positive.
>
>Although, I wonder if all these operations are only accessible to root:
>
>static const struct file_operations uinput_fops = {
>        .owner          = THIS_MODULE,
>        .open           = uinput_open,
>        .release        = uinput_release,
>        .read           = uinput_read,
>        .write          = uinput_write,
>        .poll           = uinput_poll,
>        .unlocked_ioctl = uinput_ioctl,
>#ifdef CONFIG_COMPAT
>        .compat_ioctl   = uinput_compat_ioctl,
>#endif
>        .llseek         = no_llseek,
>};

/dev/uinput must be 0600, or accessible to equally privileged user, or you'll be opening your system to much mischief.


Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: uinput - fix Spectre v1 vulnerability
From: Gustavo A. R. Silva @ 2018-10-16 18:12 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <26EEA6BC-43B0-42F7-A237-572D32EB2309@gmail.com>



On 10/16/18 8:09 PM, Dmitry Torokhov wrote:

> 
> /dev/uinput 

I've got it. This explains it all. :)

> must be 0600, or accessible to equally privileged user, or you'll be opening your system to much mischief.
> 
Thanks, Dmitry.
--
Gustavo

^ permalink raw reply

* Re: [PATCH 2/7] Input: sx8654 - add reset-gpio support
From: Richard Leitner @ 2018-10-16 19:32 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel,
	Richard Leitner
In-Reply-To: <20181016173931.GC230131@dtor-ws>

Hi Dimitry,
thanks for the quick reply!

On 10/16/18 7:39 PM, Dmitry Torokhov wrote:
> Hi Richard,
> 
> On Tue, Oct 16, 2018 at 11:16:48AM +0200, Richard Leitner wrote:
>> The sx8654 features a NRST input which may be connected to a GPIO.
>> Therefore add support for hard-resetting the sx8654 via this NRST.
>>
>> If the reset-gpio property is provided the sx8654 is resetted via NRST
>> instead of the soft-reset via I2C.
>>

...

>> +
>> +	dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
>> +	err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
>> +					SOFTRESET_VALUE);
>> +	if (err)
>> +		return err;
> 
> Have this in an "else" branch?

If that's the preferred way... of course.

> 
>> +
>> +	return 0;
>> +}
>> +
>>   static int sx8654_open(struct input_dev *dev)
>>   {
>>   	struct sx8654 *sx8654 = input_get_drvdata(dev);
>> @@ -171,6 +195,44 @@ static void sx8654_close(struct input_dev *dev)
>>   	}
>>   }
>>   
>> +#ifdef CONFIG_OF
>> +static int sx8654_get_ofdata(struct sx8654 *ts)
>> +{
>> +	struct device *dev = &ts->client->dev;
>> +	struct device_node *node = dev->of_node;
>> +	int err;
>> +
>> +	if (!node) {
>> +		ts->gpio_reset = NULL;
>> +		return 0;
>> +	}
> 
> There is no need to wrap this into CONFIG_OF or test "node". Just use
> devm_gpiod_get_optional() and it will work equally well on DT, ACPI, and
> static board systems.

Ok. Thank you for that information!
I haven't been aware of this before :-)

> 
>> +
>> +	ts->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
>> +	if (PTR_ERR(ts->gpio_reset) == -EPROBE_DEFER) {
>> +		return -EPROBE_DEFER;
>> +	} else if (IS_ERR(ts->gpio_reset)) {
>> +		err = PTR_ERR(ts->gpio_reset);
> 
> I'd do:
> 
> 		if (err != -EPROBE_DEFER)
> 			dev_err(...)
> 
> here instead of separate branch.

Indeed... Looks like a more sexy solution. Thanks.

> 
>> +		dev_err(dev, "unable to request GPIO reset pin (%d)\n", err);
>> +		return err;
>> +	}
>> +	dev_dbg(dev, "got GPIO reset pin\n");
> 
> Also, I'd move all this code directly into probe() since all this
> #ifdefery is not needed.

Without the #ifdef'ery and node checking this sounds reasonable.

> 
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id sx8654_of_match[] = {
>> +	{ .compatible = "semtech,sx8654", },
>> +	{ },
>> +};
> 
> Please keep the device table wehere it was.

OK.

>> @@ -186,10 +248,20 @@ static int sx8654_probe(struct i2c_client *client,
>>   	if (!sx8654)
>>   		return -ENOMEM;
>>   
>> +	sx8654->client = client;
>> +
>> +	error = sx8654_get_ofdata(sx8654);
>> +	if (error) {
>> +		dev_err(&client->dev, "get_ofdata failed: %d\n", error);
>> +		return error;
>> +	}
>> +
>>   	input = devm_input_allocate_device(&client->dev);
>>   	if (!input)
>>   		return -ENOMEM;
>>   
>> +	sx8654->input = input;
>> +
>>   	input->name = "SX8654 I2C Touchscreen";
>>   	input->id.bustype = BUS_I2C;
>>   	input->dev.parent = &client->dev;
>> @@ -201,15 +273,11 @@ static int sx8654_probe(struct i2c_client *client,
>>   	input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
>>   	input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
>>   
>> -	sx8654->client = client;
>> -	sx8654->input = input;
>> -
> 
> Why do we need to move this?

This was because sx8654_get_ofdata needed ts->client->dev.
But as I will integrate the reset gpio getting in the probe function 
I'll move it back.

...

> 
> Thanks.
> 

I'll send a v2 of the series as soon as I have fixed the other patches.

regards;Richard.L

^ permalink raw reply

* Re: [PATCH 6/7] Input: sx8654 - add sx8650 support
From: Richard Leitner @ 2018-10-16 19:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel,
	Richard Leitner
In-Reply-To: <20181016174838.GD230131@dtor-ws>


On 10/16/18 7:48 PM, Dmitry Torokhov wrote:
> On Tue, Oct 16, 2018 at 11:22:48AM +0200, Richard Leitner wrote:
>> The sx8654 and sx8650 are quite similar, therefore add support for the
>> sx8650 within the sx8654 driver.
>>

...

>>   
>>   /* bits for I2C_REG_CHANMASK */
>> -#define CONV_X				0x80
>> -#define CONV_Y				0x40
>> +#define CONV_X				BIT(7)
>> +#define CONV_Y				BIT(6)
> 
> If you are using BIT() you need to include include/linux/bitops.h
> 
> I also would prefer conversion to using BIT() as a separate patch.

OK. I'll take it out of this patch.

Should I convert them (and the other #defines where it makes sense) to 
BIT() at all?

> 
>>   
>>   /* coordinates rate: higher nibble of CTRL0 register */
>>   #define RATE_MANUAL			0x00
>> @@ -71,14 +75,110 @@
>>   /* power delay: lower nibble of CTRL0 register */
>>   #define POWDLY_1_1MS			0x0b
>>   
>> +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
>> + * last PENIRQ after which we assume the pen is lifted.
>> + */
>> +#define SX8650_PENIRQ_TIMEOUT		(80 * 1100 * 1000)
>> +
>>   #define MAX_12BIT			((1 << 12) - 1)
>> +#define MAX_I2C_READ_LEN		10 /* see datasheet section 5.1.5 */
>> +
>> +/* channel definition */
>> +#define CH_X				0x00
>> +#define CH_Y				0x01
>> +
>> +struct sx865x_data {
>> +	u8 cmd_manual;
>> +	u8 chan_mask;
>> +	u8 has_irq_penrelease;
>> +	u8 has_reg_irqmask;
>> +	irq_handler_t irqh;
>> +};
>>   
>>   struct sx8654 {
>>   	struct input_dev *input;
>>   	struct i2c_client *client;
>>   	struct gpio_desc *gpio_reset;
>> +	struct hrtimer timer;
> 
> Does this have to be hrtimer? Can regular timer be used?

I'll check again if it's feasible to reduce the timer delay to something 
below the "normal" jiffy. If not I'll replace the hrtimer with a timer.

> 
>> +
>> +	const struct sx865x_data *data;
>>   };
>>   
>> +static enum hrtimer_restart sx865x_penrelease_timer_handler(struct hrtimer *h)
>> +{
>> +	struct sx8654 *ts = container_of(h, struct sx8654, timer);
>> +
>> +	input_report_key(ts->input, BTN_TOUCH, 0);
>> +	input_sync(ts->input);
>> +	dev_dbg(&ts->client->dev, "penrelease by timer\n");
>> +
>> +	return HRTIMER_NORESTART;
>> +}
>> +

...

>> @@ -196,10 +312,30 @@ static void sx8654_close(struct input_dev *dev)
>>   }
>>   
>>   #ifdef CONFIG_OF
>> +static const struct of_device_id sx8654_of_match[] = {
>> +	{
>> +		.compatible = "semtech,sx8650",
>> +		.data = &sx8650_data,
>> +	}, {
>> +		.compatible = "semtech,sx8654",
>> +		.data = &sx8654_data,
>> +	}, {
>> +		.compatible = "semtech,sx8655",
>> +		.data = &sx8654_data,
>> +	}, {
>> +		.compatible = "semtech,sx8656",
>> +		.data = &sx8654_data,
>> +	}, {},
>> +};
>> +MODULE_DEVICE_TABLE(of, sx8654_of_match);
>> +
>>   static int sx8654_get_ofdata(struct sx8654 *ts)
>>   {
>>   	struct device *dev = &ts->client->dev;
>>   	struct device_node *node = dev->of_node;
>> +	const struct of_device_id *of_id = of_match_device(sx8654_of_match,
>> +							   dev);
> 
> If you use of_device_get_match_data() you do not need to move device
> table around.

Thanks for that hint. I haven't known there's something like this ;-)

> 
>> +	const struct sx865x_data *data = (struct sx865x_data *)of_id->data;
>>   	int err;
>>   
>>   	if (!node) {

...

>> @@ -327,6 +466,7 @@ static int sx8654_probe(struct i2c_client *client,
>>   }
>>   
>>   static const struct i2c_device_id sx8654_id_table[] = {
>> +	{ "semtech_sx8650", 0 },
> 
> Can we add the data here as well?

Does it generate any benefit if we add it? Who should be using it?

I found in other drivers that it's used as a fallback when 
of_device_get_match_data() returns NULL... Should I also implement it 
that way?

> 
>>   	{ "semtech_sx8654", 0 },
>>   	{ "semtech_sx8655", 0 },
>>   	{ "semtech_sx8656", 0 },
>> -- 
>> 2.11.0
>>
> 
> Thanks.
> 

thanks&regards;Richard.L

^ permalink raw reply

* Re: [PATCH 6/7] Input: sx8654 - add sx8650 support
From: Dmitry Torokhov @ 2018-10-16 20:18 UTC (permalink / raw)
  To: Richard Leitner
  Cc: robh+dt, mark.rutland, linux-input, devicetree, linux-kernel
In-Reply-To: <575f935b-e082-7d19-30c1-4db36ed6114d@skidata.com>

On Tue, Oct 16, 2018 at 09:34:26PM +0200, Richard Leitner wrote:
> 
> On 10/16/18 7:48 PM, Dmitry Torokhov wrote:
> > On Tue, Oct 16, 2018 at 11:22:48AM +0200, Richard Leitner wrote:
> > > The sx8654 and sx8650 are quite similar, therefore add support for the
> > > sx8650 within the sx8654 driver.
> > > 
> 
> ...
> 
> > >   /* bits for I2C_REG_CHANMASK */
> > > -#define CONV_X				0x80
> > > -#define CONV_Y				0x40
> > > +#define CONV_X				BIT(7)
> > > +#define CONV_Y				BIT(6)
> > 
> > If you are using BIT() you need to include include/linux/bitops.h
> > 
> > I also would prefer conversion to using BIT() as a separate patch.
> 
> OK. I'll take it out of this patch.
> 
> Should I convert them (and the other #defines where it makes sense) to BIT()
> at all?

Sure as it documents the intent better, I just asked it to be split out
since each patch should solve one particular problem.

> 
> > 
> > >   /* coordinates rate: higher nibble of CTRL0 register */
> > >   #define RATE_MANUAL			0x00
> > > @@ -71,14 +75,110 @@
> > >   /* power delay: lower nibble of CTRL0 register */
> > >   #define POWDLY_1_1MS			0x0b
> > > +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
> > > + * last PENIRQ after which we assume the pen is lifted.
> > > + */
> > > +#define SX8650_PENIRQ_TIMEOUT		(80 * 1100 * 1000)
> > > +
> > >   #define MAX_12BIT			((1 << 12) - 1)
> > > +#define MAX_I2C_READ_LEN		10 /* see datasheet section 5.1.5 */
> > > +
> > > +/* channel definition */
> > > +#define CH_X				0x00
> > > +#define CH_Y				0x01
> > > +
> > > +struct sx865x_data {
> > > +	u8 cmd_manual;
> > > +	u8 chan_mask;
> > > +	u8 has_irq_penrelease;
> > > +	u8 has_reg_irqmask;
> > > +	irq_handler_t irqh;
> > > +};
> > >   struct sx8654 {
> > >   	struct input_dev *input;
> > >   	struct i2c_client *client;
> > >   	struct gpio_desc *gpio_reset;
> > > +	struct hrtimer timer;
> > 
> > Does this have to be hrtimer? Can regular timer be used?
> 
> I'll check again if it's feasible to reduce the timer delay to something
> below the "normal" jiffy. If not I'll replace the hrtimer with a timer.

That means polling faster than 200HZ in the best case. I think it is too
much.

> 
> > 
> > > +
> > > +	const struct sx865x_data *data;
> > >   };
> > > +static enum hrtimer_restart sx865x_penrelease_timer_handler(struct hrtimer *h)
> > > +{
> > > +	struct sx8654 *ts = container_of(h, struct sx8654, timer);
> > > +
> > > +	input_report_key(ts->input, BTN_TOUCH, 0);
> > > +	input_sync(ts->input);
> > > +	dev_dbg(&ts->client->dev, "penrelease by timer\n");
> > > +
> > > +	return HRTIMER_NORESTART;
> > > +}
> > > +
> 
> ...
> 
> > > @@ -196,10 +312,30 @@ static void sx8654_close(struct input_dev *dev)
> > >   }
> > >   #ifdef CONFIG_OF
> > > +static const struct of_device_id sx8654_of_match[] = {
> > > +	{
> > > +		.compatible = "semtech,sx8650",
> > > +		.data = &sx8650_data,
> > > +	}, {
> > > +		.compatible = "semtech,sx8654",
> > > +		.data = &sx8654_data,
> > > +	}, {
> > > +		.compatible = "semtech,sx8655",
> > > +		.data = &sx8654_data,
> > > +	}, {
> > > +		.compatible = "semtech,sx8656",
> > > +		.data = &sx8654_data,
> > > +	}, {},
> > > +};
> > > +MODULE_DEVICE_TABLE(of, sx8654_of_match);
> > > +
> > >   static int sx8654_get_ofdata(struct sx8654 *ts)
> > >   {
> > >   	struct device *dev = &ts->client->dev;
> > >   	struct device_node *node = dev->of_node;
> > > +	const struct of_device_id *of_id = of_match_device(sx8654_of_match,
> > > +							   dev);
> > 
> > If you use of_device_get_match_data() you do not need to move device
> > table around.
> 
> Thanks for that hint. I haven't known there's something like this ;-)
> 
> > 
> > > +	const struct sx865x_data *data = (struct sx865x_data *)of_id->data;
> > >   	int err;
> > >   	if (!node) {
> 
> ...
> 
> > > @@ -327,6 +466,7 @@ static int sx8654_probe(struct i2c_client *client,
> > >   }
> > >   static const struct i2c_device_id sx8654_id_table[] = {
> > > +	{ "semtech_sx8650", 0 },
> > 
> > Can we add the data here as well?
> 
> Does it generate any benefit if we add it? Who should be using it?

If someone decides to use driver in non-DT environment.

> 
> I found in other drivers that it's used as a fallback when
> of_device_get_match_data() returns NULL... Should I also implement it that
> way?

Yes, that would be good.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: WARNING: kmalloc bug in input_mt_init_slots
From: Dmitry Torokhov @ 2018-10-17  0:09 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Christopher Lameter, Dmitry Vyukov, syzbot+87829a10073277282ad1,
	Pekka Enberg, linux-input@vger.kernel.org, lkml, Henrik Rydberg,
	syzkaller-bugs, Linux-MM
In-Reply-To: <20180927143537.GB19006@bombadil.infradead.org>

On Thu, Sep 27, 2018 at 07:35:37AM -0700, Matthew Wilcox wrote:
> On Mon, Sep 24, 2018 at 11:41:58AM -0700, Dmitry Torokhov wrote:
> > > How large is the allocation? AFACIT nRequests larger than KMALLOC_MAX_SIZE
> > > are larger than the maximum allowed by the page allocator. Thus the warning
> > > and the NULL return.
> > 
> > The size in this particular case is being derived from a value passed
> > from userspace. Input core does not care about any limits on size of
> > memory kmalloc() can support and is perfectly happy with getting NULL
> > and telling userspace to go away with their silly requests by returning
> > -ENOMEM.
> > 
> > For the record: I definitely do not want to pre-sanitize size neither in
> > uinput nor in input core.
> 
> Probably should be using kvzalloc then.

No. No sane input device can track so many contacts so we need to use
kvzalloc(). Failing to allocate memory is proper response here.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: xen-kbdfront - mark expected switch fall-through
From: Dmitry Torokhov @ 2018-10-17  0:12 UTC (permalink / raw)
  To: Gustavo A. R. Silva; +Cc: linux-input, linux-kernel
In-Reply-To: <20181008140748.GA10114@embeddedor.com>

On Mon, Oct 08, 2018 at 04:07:48PM +0200, Gustavo A. R. Silva wrote:
> In preparation to enabling -Wimplicit-fallthrough, mark switch cases
> where we are expecting to fall through.
> 
> Notice that in this particular case, I placed the "fall through"
> part at the beginning of the code comment, which is what GCC is
> expecting to find.
> 
> Addresses-Coverity-ID: 114757 ("Missing break in switch")
> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>

Applied, thank you.

> ---
>  drivers/input/misc/xen-kbdfront.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
> index 594f72e..24bc5c5 100644
> --- a/drivers/input/misc/xen-kbdfront.c
> +++ b/drivers/input/misc/xen-kbdfront.c
> @@ -524,7 +524,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
>  	case XenbusStateClosed:
>  		if (dev->state == XenbusStateClosed)
>  			break;
> -		/* Missed the backend's CLOSING state -- fallthrough */
> +		/* fall through - Missed the backend's CLOSING state */
>  	case XenbusStateClosing:
>  		xenbus_frontend_closed(dev);
>  		break;
> -- 
> 2.7.4
> 

-- 
Dmitry

^ permalink raw reply

* [PATCH] Input: synaptics - avoid using uninitialized variable when probing
From: Dmitry Torokhov @ 2018-10-17  0:14 UTC (permalink / raw)
  To: linux-input; +Cc: Benjamin Tissoires, linux-kernel

synaptics_detect() does not check whether sending commands to the
device succeeds and instead relies on getting unique data from the
device. Let's make sure we seed entire buffer with zeroes to make sure
we not use garbage on stack that just happen to be 0x47.

Reported-by: syzbot+13cb3b01d0784e4ffc3f@syzkaller.appspotmail.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/mouse/synaptics.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 55d33500d55e..5e85f3cca867 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -99,9 +99,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
 int synaptics_detect(struct psmouse *psmouse, bool set_properties)
 {
 	struct ps2dev *ps2dev = &psmouse->ps2dev;
-	u8 param[4];
-
-	param[0] = 0;
+	u8 param[4] = { 0 };
 
 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
-- 
2.19.1.331.ge82ca0e54c-goog


-- 
Dmitry

^ permalink raw reply related

* [RESEND PATCH V7 1/3] MAINTAINERS: da7280 updates to the Dialog Semiconductor search terms
From: Roy Im @ 2018-10-17  0:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1539737877.git.Roy.Im@diasemi.com>


This patch adds the da7280 bindings doc and driver to the Dialog
Semiconductor support list.

Signed-off-by: Roy Im <roy.im.opensource@diasemi.com>

---
v7: No changes.
v6: No changes.
v5: No changes.
v4: No changes.
v3: No changes.
v2: No changes.


 MAINTAINERS |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b22e7fd..1af587f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4313,6 +4313,7 @@ S:	Supported
 F:	Documentation/hwmon/da90??
 F:	Documentation/devicetree/bindings/mfd/da90*.txt
 F:	Documentation/devicetree/bindings/input/da90??-onkey.txt
+F:	Documentation/devicetree/bindings/input/dlg,da72??.txt
 F:	Documentation/devicetree/bindings/thermal/da90??-thermal.txt
 F:	Documentation/devicetree/bindings/regulator/da92*.txt
 F:	Documentation/devicetree/bindings/watchdog/da90??-wdt.txt
@@ -4321,6 +4322,7 @@ F:	drivers/gpio/gpio-da90??.c
 F:	drivers/hwmon/da90??-hwmon.c
 F:	drivers/iio/adc/da91??-*.c
 F:	drivers/input/misc/da90??_onkey.c
+F:	drivers/input/misc/da72??.[ch]
 F:	drivers/input/touchscreen/da9052_tsi.c
 F:	drivers/leds/leds-da90??.c
 F:	drivers/mfd/da903x.c
-- 
end-of-patch for RESEND PATCH V7

^ permalink raw reply related

* [RESEND PATCH V7 0/3]  da7280: haptic driver submission
From: Roy Im @ 2018-10-17  0:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel

This patch adds support for the Dialog DA7280 Haptic driver IC.

In this patch set the following is provided:

[PATCH V7 1/3] MAINTAINERS file update for DA7280
[PATCH V7 2/3] DA7280 DT Binding
[PATCH V7 3/3] DA7280 Driver

This patch applies against linux-next and v4.19-rc6

Thank you,
Roy Im, Dialog Semiconductor Ltd.

Roy Im (3):
  MAINTAINERS: da7280 updates to the Dialog Semiconductor search terms
  dt-bindings: input: Add document bindings for DA7280
  Input: new da7280 haptic driver

 .../devicetree/bindings/input/dlg,da7280.txt       |  105 ++
 MAINTAINERS                                        |    2 +
 drivers/input/misc/Kconfig                         |   13 +
 drivers/input/misc/Makefile                        |    1 +
 drivers/input/misc/da7280.c                        | 1409 ++++++++++++++++++++
 drivers/input/misc/da7280.h                        |  412 ++++++
 6 files changed, 1942 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/dlg,da7280.txt
 create mode 100644 drivers/input/misc/da7280.c
 create mode 100644 drivers/input/misc/da7280.h

-- 
end-of-patch for RESEND PATCH V7

^ permalink raw reply

* [RESEND PATCH V7 2/3] dt-bindings: input: Add document bindings for DA7280
From: Roy Im @ 2018-10-17  0:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1539737877.git.Roy.Im@diasemi.com>


Add device tree binding information for DA7280 haptic driver.
Example bindings for DA7280 are added.

Reviewed-by: Rob Herring <robh@kernel.org>.

Signed-off-by: Roy Im <roy.im.opensource@diasemi.com>

---
v7: No changes.
v6: No changes.
v5: Updated descriptions and fixed errors.
v4: Fixed commit message, properties.
v3: Fixed subject format.
v2: No changes


 .../devicetree/bindings/input/dlg,da7280.txt       |  105 ++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/dlg,da7280.txt

diff --git a/Documentation/devicetree/bindings/input/dlg,da7280.txt b/Documentation/devicetree/bindings/input/dlg,da7280.txt
new file mode 100644
index 0000000..a25a12f
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/dlg,da7280.txt
@@ -0,0 +1,105 @@
+Dialog Semiconductor DA7280 Haptics bindings
+
+Required properties:
+- compatible: Should be "dlg,da7280".
+- reg: Specifies the I2C slave address.
+
+- interrupt-parent : Specifies the phandle of the interrupt controller to
+  which the IRQs from DA7280 are delivered to.
+
+- dlg,actuator-type: Set Actuator type. it should be one of:
+  "LRA" - Linear Resonance Actuator type.
+  "ERM-bar" - Bar type Eccentric Rotating Mass.
+  "ERM-coin" - Coin type Eccentric Rotating Mass.
+
+- dlg,op-mode: Haptic operation mode.
+  Possible values:
+	1 - Direct register override(DRO) mode triggered by i2c(default),
+	2 - PWM data source mode controlled by PWM duty,
+	3 - Register triggered waveform memory(RTWM) mode, the pattern
+	    assigned to the PS_SEQ_ID played as much times as PS_SEQ_LOOP,
+	4 - Edge triggered waveform memory(ETWM) mode, external GPI(N)
+	    control are required to enable/disable and it needs to keep
+	    device enabled by sending magnitude (X > 0),
+	    the pattern is assigned to the GPI(N)_SEQUENCE_ID below.
+	For more details, please see the datasheet.
+
+- dlg,nom-microvolt: Nominal actuator voltage rating.
+  Valid values: 0 - 6000000.
+- dlg,abs-max-microvolt: Absolute actuator maximum voltage rating.
+  Valid values: 0 - 6000000.
+- dlg,imax-microamp: Actuator max current rating.
+  Valid values: 0 - 252000.
+  Default: 130000.
+- dlg,impd-micro-ohms: the impedance of the actuator in micro ohms.
+  Valid values: 0 - 1500000000.
+
+Optional properties:
+- pwms : phandle to the physical PWM(Pulse Width Modulation) device.
+  PWM properties should be named "pwms". And number of cell is different
+  for each pwm device.
+  (See Documentation/devicetree/bindings/pwm/pwm.txt
+   for further information relating to pwm properties)
+
+- dlg,ps-seq-id: the PS_SEQ_ID(pattern ID in waveform memory inside chip)
+  to play back when RTWM-MODE is enabled.
+  Valid range: 0 - 15.
+- dlg,ps-seq-loop: the PS_SEQ_LOOP, Number of times the pre-stored sequence
+  pointed to by PS_SEQ_ID or GPI(N)_SEQUENCE_ID is repeated.
+  Valid range: 0 - 15.
+- dlg,gpiN-seq-id: the GPI(N)_SEQUENCE_ID, pattern to play
+  when gpi0 is triggered, 'N' must be 0 - 2.
+  Valid range: 0 - 15.
+- dlg,gpiN-mode: the pattern mode which can select either
+  "Single-pattern" or "Multi-pattern", 'N' must be 0 - 2.
+- dlg,gpiN-polarity: gpiN polarity which can be chosen among
+  "Rising-edge", "Falling-edge" and "Both-edge",
+  'N' must be 0 - 2
+  Haptic will work by this edge option in case of ETWM mode.
+
+- dlg,resonant-freq-hz: use in case of LRA.
+  the frequency range: 50 - 300.
+  Default: 205.
+
+- dlg,bemf-sens-enable: Enable for internal loop computations.
+- dlg,freq-track-enable: Enable for resonant frequency tracking.
+- dlg,acc-enable: Enable for active acceleration.
+- dlg,rapid-stop-enable: Enable for rapid stop.
+- dlg,amp-pid-enable: Enable for the amplitude PID.
+- dlg,mem-array: Customized waveform memory(patterns) data downloaded to
+  the device during initialization. This is an array of 100 values(u8).
+
+For further information, see device datasheet.
+
+======
+
+Example:
+
+	haptics: da7280-haptics@4a {
+		compatible = "dlg,da7280";
+		reg = <0x4a>;
+		interrupt-parent = <&gpio6>;
+		interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+		dlg,actuator-type = "LRA";
+		dlg,op-mode = <1>;
+		dlg,nom-microvolt = <2000000>;
+		dlg,abs-max-microvolt = <2000000>;
+		dlg,imax-microamp = <170000>;
+		dlg,resonant-freq-hz = <180>;
+		dlg,impd-micro-ohms = <10500000>;
+		dlg,freq-track-enable;
+		dlg,rapid-stop-enable;
+		dlg,mem-array = <
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 		  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+		>;
+
+	};
-- 
end-of-patch for RESEND PATCH V7

^ permalink raw reply related

* [RESEND PATCH V7 3/3] Input: new da7280 haptic driver
From: Roy Im @ 2018-10-17  0:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1539737877.git.Roy.Im@diasemi.com>


Adds support for the Dialog DA7280 LRA/ERM Haptic Driver with
multiple mode and integrated waveform memory and wideband support.
It communicates via an I2C bus to the device.

Signed-off-by: Roy Im <roy.im.opensource@diasemi.com>

---
v7: 
	- Added more attributes to handle one value per file.
	- Replaced and updated the dt-related code and functions called.
	- Fixed error/functions.
	- Rebased to v4.19-rc6.
v6: No changes.
v5: Fixed errors in Kconfig file.
v4: Updated code as dt-bindings are changed.
v3: No changes.
v2: Fixed kbuild error/warning


 drivers/input/misc/Kconfig  |   13 +
 drivers/input/misc/Makefile |    1 +
 drivers/input/misc/da7280.c | 1409 +++++++++++++++++++++++++++++++++++++++++++
 drivers/input/misc/da7280.h |  412 +++++++++++++
 4 files changed, 1835 insertions(+)
 create mode 100644 drivers/input/misc/da7280.c
 create mode 100644 drivers/input/misc/da7280.h

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2b..751cac6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -851,4 +851,17 @@ config INPUT_SC27XX_VIBRA
 	  To compile this driver as a module, choose M here. The module will
 	  be called sc27xx_vibra.
 
+config INPUT_DA7280_HAPTICS
+	tristate "Dialog Semiconductor DA7280 haptics support"
+	depends on INPUT && I2C
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the Dialog DA7280 haptics driver.
+	  The haptics can be controlled by i2c communication,
+	  or by PWM input, or by GPI.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da7280.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1..d941348 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000)		+= cma3000_d0x.o
 obj-$(CONFIG_INPUT_CMA3000_I2C)		+= cma3000_d0x_i2c.o
 obj-$(CONFIG_INPUT_COBALT_BTNS)		+= cobalt_btns.o
 obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON)	+= cpcap-pwrbutton.o
+obj-$(CONFIG_INPUT_DA7280_HAPTICS)	+= da7280.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)	+= da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
 obj-$(CONFIG_INPUT_DA9063_ONKEY)	+= da9063_onkey.o
diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c
new file mode 100644
index 0000000..b1072ef
--- /dev/null
+++ b/drivers/input/misc/da7280.c
@@ -0,0 +1,1409 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DA7280 Haptic device driver
+ *
+ * Copyright (c) 2018 Dialog Semiconductor.
+ * Author: Roy Im <Roy.Im.Opensource@diasemi.com>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include "da7280.h"
+
+/* uV unit for voltage rate */
+#define DA7280_VOLTAGE_RATE_MAX		6000000
+#define DA7280_VOLTAGE_RATE_STEP	23400
+#define DA7280_NOMMAX_DFT		0x6B
+#define DA7280_ABSMAX_DFT		0x78
+
+#define DA7280_IMPD_MAX			1500000000
+#define DA7280_IMPD_DEFAULT		22000000
+
+#define DA7280_IMAX_DEFAULT		0x0E
+/* uA unit step and limit for IMAX*/
+#define DA7280_IMAX_STEP		7200
+#define DA7280_IMAX_LIMIT		252000
+
+#define DA7280_RESONT_FREQH_DFT		0x39
+#define DA7280_RESONT_FREQL_DFT		0x32
+#define DA7280_MIN_RESONAT_FREQ_HZ	50
+#define DA7280_MAX_RESONAT_FREQ_HZ	300
+#define DA7280_MIN_PWM_FREQ_KHZ		10
+#define DA7280_MAX_PWM_FREQ_KHZ		250
+
+#define DA7280_SEQ_ID_MAX		15
+#define DA7280_SEQ_LOOP_MAX		15
+#define DA7280_GPI1_SEQ_ID_DEFT	0x0
+
+#define DA7280_SNP_MEM_SIZE		100
+#define DA7280_SNP_MEM_MAX		DA7280_SNP_MEM_99
+
+#define IRQ_NUM				3
+
+#define DA7280_SKIP_INIT		0x100
+
+enum da7280_haptic_dev_t {
+	DA7280_LRA	= 0,
+	DA7280_ERM_BAR	= 1,
+	DA7280_ERM_COIN	= 2,
+	DA7280_DEV_MAX,
+};
+
+enum da7280_op_mode {
+	DA7280_INACTIVE		= 0,
+	DA7280_DRO_MODE		= 1,
+	DA7280_PWM_MODE		= 2,
+	DA7280_RTWM_MODE	= 3,
+	DA7280_ETWM_MODE	= 4,
+	DA7280_OPMODE_MAX,
+};
+
+struct da7280_gpi_ctl {
+	u8 seq_id;
+	u8 mode;
+	u8 polarity;
+};
+
+struct da7280_haptic {
+	struct regmap *regmap;
+	struct input_dev *input_dev;
+	struct device *dev;
+	struct i2c_client *client;
+	struct pwm_device *pwm_dev;
+	bool	legacy;
+	int pwm_id;
+	struct work_struct work;
+
+	unsigned int magnitude;
+
+	u8 dev_type;
+	u8 op_mode;
+	u16 nommax;
+	u16 absmax;
+	u32 imax;
+	u32 impd;
+	u32 resonant_freq_h;
+	u32 resonant_freq_l;
+	bool bemf_sense_en;
+	bool freq_track_en;
+	bool acc_en;
+	bool rapid_stop_en;
+	bool amp_pid_en;
+	u8 ps_seq_id;
+	u8 ps_seq_loop;
+	struct da7280_gpi_ctl gpi_ctl[3];
+	bool mem_update;
+	u8 snp_mem[DA7280_SNP_MEM_SIZE];
+	u8 enabled;
+};
+
+static bool da7280_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DA7280_IRQ_EVENT1:
+	case DA7280_IRQ_EVENT_WARNING_DIAG:
+	case DA7280_IRQ_EVENT_SEQ_DIAG:
+	case DA7280_IRQ_STATUS1:
+	case DA7280_TOP_CTL1:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config da7280_haptic_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = DA7280_SNP_MEM_MAX,
+	.volatile_reg = da7280_volatile_register,
+};
+
+static int da7280_haptic_mem_update(struct da7280_haptic *haptics)
+{
+	int error;
+	unsigned int val;
+
+	/* It is recommended to update the patterns
+	 * during haptic is not working in order to avoid conflict
+	 */
+	error = regmap_read(haptics->regmap, DA7280_IRQ_STATUS1, &val);
+	if (error)
+		return error;
+	if (val & DA7280_STA_WARNING_MASK) {
+		dev_warn(haptics->dev,
+			 "Warning! Please check HAPTIC status.\n");
+		return -EBUSY;
+	}
+
+	/* Patterns are not updated if the lock bit is enabled */
+	val = 0;
+	error = regmap_read(haptics->regmap, DA7280_MEM_CTL2, &val);
+	if (error)
+		return error;
+	if (~val & DA7280_WAV_MEM_LOCK_MASK) {
+		dev_warn(haptics->dev,
+			 "Please unlock the bit first\n");
+		return -EACCES;
+	}
+
+	/* Set to Inactive mode to make sure safety */
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_OPERATION_MODE_MASK,
+				   0);
+	if (error)
+		return error;
+
+	error = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
+	if (error)
+		return error;
+
+	return regmap_bulk_write(haptics->regmap, val,
+			haptics->snp_mem, DA7280_SNP_MEM_MAX - val + 1);
+}
+
+static int da7280_haptic_set_pwm(struct da7280_haptic *haptics)
+{
+	struct pwm_args pargs;
+	u64 period_mag_multi;
+	unsigned int pwm_duty;
+	int error;
+
+	pwm_get_args(haptics->pwm_dev, &pargs);
+	period_mag_multi =
+		(u64)(pargs.period * haptics->magnitude);
+	if (haptics->acc_en)
+		pwm_duty =
+			(unsigned int)(period_mag_multi >> 16);
+	else
+		pwm_duty =
+			(unsigned int)((period_mag_multi >> 16)
+				+ pargs.period) / 2;
+
+	error = pwm_config(haptics->pwm_dev,
+			   pwm_duty, pargs.period);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to configure pwm : %d\n", error);
+		return error;
+	}
+
+	error = pwm_enable(haptics->pwm_dev);
+	if (error) {
+		pwm_disable(haptics->pwm_dev);
+		dev_err(haptics->dev,
+			"failed to enable haptics pwm device : %d\n", error);
+	}
+
+	return error;
+}
+
+static void da7280_haptic_enable(struct da7280_haptic *haptics)
+{
+	int error = 0;
+
+	if (haptics->enabled)
+		return;
+
+	switch (haptics->op_mode) {
+	case DA7280_DRO_MODE:
+		/* the valid range check when acc_en is enabled */
+		if (haptics->acc_en && haptics->magnitude > 0x7F)
+			haptics->magnitude = 0x7F;
+		else if (haptics->magnitude > 0xFF)
+			haptics->magnitude = 0xFF;
+
+		/* Set driver level
+		 * as a % of ACTUATOR_NOMMAX(nommax)
+		 */
+		error = regmap_write(haptics->regmap,
+				     DA7280_TOP_CTL2,
+				     haptics->magnitude);
+		if (error) {
+			dev_err(haptics->dev,
+				"i2c err for driving level set : %d\n",
+				error);
+			return;
+		}
+		break;
+	case DA7280_PWM_MODE:
+		if (da7280_haptic_set_pwm(haptics))
+			return;
+		break;
+	case DA7280_RTWM_MODE:
+		/* PS_SEQ_ID will be played
+		 * as many times as the PS_SEQ_LOOP
+		 */
+	case DA7280_ETWM_MODE:
+		/* Now users are able to control the GPI(N)
+		 * assigned to GPI_0, GPI1 and GPI2 accordingly
+		 * please see the datasheet for details.
+		 * GPI(N)_SEQUENCE_ID will be played
+		 * as many times as the PS_SEQ_LOOP
+		 */
+		break;
+	default:
+		dev_err(haptics->dev,
+			"Invalid Mode(%d)\n", haptics->op_mode);
+		return;
+	}
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_OPERATION_MODE_MASK,
+				   haptics->op_mode);
+	if (error) {
+		dev_err(haptics->dev,
+			"i2c err for op_mode setting : %d\n", error);
+		return;
+	}
+
+	if (haptics->op_mode == DA7280_PWM_MODE ||
+	    haptics->op_mode == DA7280_RTWM_MODE) {
+		error = regmap_update_bits(haptics->regmap,
+					   DA7280_TOP_CTL1,
+					   DA7280_SEQ_START_MASK,
+					   DA7280_SEQ_START_MASK);
+		if (error) {
+			dev_err(haptics->dev,
+				"i2c err for sequence triggering : %d\n",
+				error);
+			return;
+		}
+	}
+
+	haptics->enabled = true;
+}
+
+static void da7280_haptic_disable(struct da7280_haptic *haptics)
+{
+	int error;
+
+	if (!haptics->enabled)
+		return;
+
+	/* Set to Inactive mode */
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_OPERATION_MODE_MASK, 0);
+	if (error) {
+		dev_err(haptics->dev,
+			"i2c err for op_mode off : %d\n", error);
+		return;
+	}
+
+	switch (haptics->op_mode) {
+	case DA7280_RTWM_MODE:
+	case DA7280_ETWM_MODE:
+		error = regmap_update_bits(haptics->regmap,
+					   DA7280_TOP_CTL1,
+					   DA7280_SEQ_START_MASK, 0);
+		if (error) {
+			dev_err(haptics->dev,
+				"i2c err for RTWM or ETWM mode off : %d\n",
+				error);
+			return;
+		}
+		break;
+	case DA7280_DRO_MODE:
+		error = regmap_write(haptics->regmap,
+				     DA7280_TOP_CTL2, 0);
+		if (error) {
+			dev_err(haptics->dev,
+				"i2c err for DRO mode off : %d\n",
+				error);
+			return;
+		}
+		break;
+	case DA7280_PWM_MODE:
+		pwm_disable(haptics->pwm_dev);
+		break;
+	default:
+		dev_err(haptics->dev,
+			"Invalid Mode(%d)\n", haptics->op_mode);
+		break;
+	}
+
+	if (haptics->op_mode < DA7280_OPMODE_MAX)
+		haptics->enabled = false;
+}
+
+static void da7280_haptic_work(struct work_struct *work)
+{
+	struct da7280_haptic *haptics =
+			container_of(work, struct da7280_haptic, work);
+
+	if (haptics->magnitude)
+		da7280_haptic_enable(haptics);
+	else
+		da7280_haptic_disable(haptics);
+}
+
+static int da7280_haptic_play(struct input_dev *dev, void *data,
+			      struct ff_effect *effect)
+{
+	struct da7280_haptic *haptics = input_get_drvdata(dev);
+
+	if (effect->u.rumble.strong_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.strong_magnitude;
+	else if (effect->u.rumble.weak_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.weak_magnitude;
+	else
+		haptics->magnitude = 0;
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static int da7280_haptic_open(struct input_dev *dev)
+{
+	struct da7280_haptic *haptics = input_get_drvdata(dev);
+	int error;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_STANDBY_EN_MASK,
+				   DA7280_STANDBY_EN_MASK);
+	if (error)
+		dev_err(haptics->dev,
+			"Failed to open haptic, i2c error : %d\n", error);
+
+	return error;
+}
+
+static void da7280_haptic_close(struct input_dev *dev)
+{
+	struct da7280_haptic *haptics = input_get_drvdata(dev);
+	int error;
+
+	cancel_work_sync(&haptics->work);
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_OPERATION_MODE_MASK, 0);
+	if (error)
+		goto error_i2c;
+
+	if (haptics->op_mode == DA7280_DRO_MODE) {
+		error = regmap_write(haptics->regmap,
+				     DA7280_TOP_CTL2, 0);
+
+		if (error)
+			goto error_i2c;
+	}
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_STANDBY_EN_MASK, 0);
+	if (error)
+		goto error_i2c;
+
+	return;
+
+error_i2c:
+	dev_err(haptics->dev, "DA7280-haptic i2c error : %d\n", error);
+}
+
+static u8 da7280_haptic_of_mode_str(struct device *dev,
+				    const char *str)
+{
+	if (!strcmp(str, "LRA"))
+		return DA7280_LRA;
+	else if (!strcmp(str, "ERM-bar"))
+		return DA7280_ERM_BAR;
+	else if (!strcmp(str, "ERM-coin"))
+		return DA7280_ERM_COIN;
+
+	dev_warn(dev, "Invalid string - set to default\n");
+	return DA7280_LRA;
+}
+
+static u8 da7280_haptic_of_gpi_mode_str(struct device *dev,
+					const char *str)
+{
+	if (!strcmp(str, "Single-pattern"))
+		return 0;
+	else if (!strcmp(str, "Multi-pattern"))
+		return 1;
+
+	dev_warn(dev, "Invalid string - set to default\n");
+	return 0;
+}
+
+static u8 da7280_haptic_of_gpi_pol_str(struct device *dev,
+				       const char *str)
+{
+	if (!strcmp(str, "Rising-edge"))
+		return 0;
+	else if (!strcmp(str, "Falling-edge"))
+		return 1;
+	else if (!strcmp(str, "Both-edge"))
+		return 2;
+
+	dev_warn(dev, "Invalid string - set to default\n");
+	return 0;
+}
+
+static u8 da7280_haptic_of_volt_rating_set(u32 val)
+{
+	u32 voltage;
+
+	voltage = val / DA7280_VOLTAGE_RATE_STEP + 1;
+
+	if (voltage > 0xFF)
+		return 0xFF;
+	return (u8)voltage;
+}
+
+static void da7280_parse_properties(struct device *dev,
+				    struct da7280_haptic *haptics)
+{
+	char gpi_str1[] = "dlg,gpi0-seq-id";
+	char gpi_str2[] = "dlg,gpi0-mode";
+	char gpi_str3[] = "dlg,gpi0-polarity";
+	unsigned int i, mem[DA7280_SNP_MEM_SIZE];
+	const char *str;
+	u32 val;
+
+	if (!device_property_read_string(dev, "dlg,actuator-type", &str))
+		haptics->dev_type =
+			da7280_haptic_of_mode_str(dev, str);
+	else /* if no property, then use the mode inside chip */
+		haptics->dev_type = DA7280_DEV_MAX;
+
+	if (device_property_read_u32(dev, "dlg,op-mode", &val) >= 0)
+		if (val > 0 && val < DA7280_OPMODE_MAX)
+			haptics->op_mode = val;
+		else
+			haptics->op_mode = DA7280_DRO_MODE;
+	else
+		haptics->op_mode = DA7280_DRO_MODE;
+
+	if (device_property_read_u32(dev, "dlg,nom-microvolt", &val) >= 0)
+		if (val < DA7280_VOLTAGE_RATE_MAX)
+			haptics->nommax =
+				da7280_haptic_of_volt_rating_set(val);
+		else
+			haptics->nommax = DA7280_SKIP_INIT;
+	else /* if no property, then use the value inside chip */
+		haptics->nommax = DA7280_SKIP_INIT;
+
+	if (device_property_read_u32(dev, "dlg,abs-max-microvolt",
+				     &val) >= 0)
+		if (val < DA7280_VOLTAGE_RATE_MAX)
+			haptics->absmax =
+				da7280_haptic_of_volt_rating_set(val);
+		else
+			haptics->absmax = DA7280_SKIP_INIT;
+	else
+		haptics->absmax = DA7280_SKIP_INIT;
+
+	if (device_property_read_u32(dev, "dlg,imax-microamp", &val) >= 0)
+		if (val < DA7280_IMAX_LIMIT)
+			haptics->imax = (val - 28600)
+					/ DA7280_IMAX_STEP + 1;
+		else
+			haptics->imax = DA7280_IMAX_DEFAULT;
+	else
+		haptics->imax = DA7280_IMAX_DEFAULT;
+
+	if (device_property_read_u32(dev, "dlg,impd-micro-ohms", &val) >= 0)
+		if (val <= DA7280_IMPD_MAX)
+			haptics->impd = val;
+		else
+			haptics->impd = DA7280_IMPD_DEFAULT;
+	else
+		haptics->impd = DA7280_IMPD_DEFAULT;
+
+	if (device_property_read_u32(dev, "dlg,resonant-freq-hz",
+				     &val) >= 0) {
+		if (val < DA7280_MAX_RESONAT_FREQ_HZ &&
+		    val > DA7280_MIN_RESONAT_FREQ_HZ) {
+			haptics->resonant_freq_h =
+				((1000000000 / (val * 1333)) >> 7) & 0xFF;
+			haptics->resonant_freq_l =
+				(1000000000 / (val * 1333)) & 0x7F;
+		} else {
+			haptics->resonant_freq_h =
+				DA7280_RESONT_FREQH_DFT;
+			haptics->resonant_freq_l =
+				DA7280_RESONT_FREQL_DFT;
+		}
+	} else {
+		haptics->resonant_freq_h = DA7280_SKIP_INIT;
+		haptics->resonant_freq_l = DA7280_SKIP_INIT;
+	}
+
+	if (device_property_read_u32(dev, "dlg,ps-seq-id", &val) >= 0)
+		if (val <= DA7280_SEQ_ID_MAX)
+			haptics->ps_seq_id = val;
+		else
+			haptics->ps_seq_id = 0;
+	else /* if no property, set to zero as a default do nothing */
+		haptics->ps_seq_id = 0;
+
+	if (device_property_read_u32(dev, "dlg,ps-seq-loop", &val) >= 0)
+		if (val <= DA7280_SEQ_LOOP_MAX)
+			haptics->ps_seq_loop = val;
+		else
+			haptics->ps_seq_loop = 0;
+	else /* if no property, then do nothing */
+		haptics->ps_seq_loop = 0;
+
+	/* GPI0~2 Control */
+	for (i = 0; i < 3; i++) {
+		gpi_str1[7] = '0' + i;
+		if (device_property_read_u32 (dev, gpi_str1, &val) >= 0)
+			if (val <= DA7280_SEQ_ID_MAX)
+				haptics->gpi_ctl[i].seq_id = val;
+			else
+				haptics->gpi_ctl[i].seq_id =
+					DA7280_GPI1_SEQ_ID_DEFT + i;
+		else /* if no property, then do nothing */
+			haptics->gpi_ctl[i].seq_id =
+				DA7280_GPI1_SEQ_ID_DEFT + i;
+
+		gpi_str2[7] = '0' + i;
+		if (!device_property_read_string(dev, gpi_str2, &str))
+			haptics->gpi_ctl[i].mode =
+				da7280_haptic_of_gpi_mode_str(dev, str);
+		else
+			haptics->gpi_ctl[i].mode = 0;
+
+		gpi_str3[7] = '0' + i;
+		if (!device_property_read_string(dev, gpi_str3, &str))
+			haptics->gpi_ctl[i].polarity =
+				da7280_haptic_of_gpi_pol_str(dev, str);
+		else
+			haptics->gpi_ctl[i].polarity = 0;
+	}
+
+	haptics->bemf_sense_en =
+		device_property_read_bool(dev, "dlg,bemf-sens-enable");
+	haptics->freq_track_en =
+		device_property_read_bool(dev, "dlg,freq-track-enable");
+	haptics->acc_en =
+		device_property_read_bool(dev, "dlg,acc-enable");
+	haptics->rapid_stop_en =
+		device_property_read_bool(dev, "dlg,rapid-stop-enable");
+	haptics->amp_pid_en =
+		device_property_read_bool(dev, "dlg,amp-pid-enable");
+
+	if (device_property_read_u32_array(dev, "dlg,mem-array",
+					   &mem[0],
+					   DA7280_SNP_MEM_SIZE) >= 0) {
+		haptics->mem_update = true;
+		for (i = 0; i < DA7280_SNP_MEM_SIZE; i++) {
+			if (mem[i] > 0xff)
+				haptics->snp_mem[i] = 0x0;
+			else
+				haptics->snp_mem[i] = (u8)mem[i];
+		}
+	} else {
+		haptics->mem_update = false;
+	}
+}
+
+static irqreturn_t da7280_irq_handler(int irq, void *data)
+{
+	struct da7280_haptic *haptics = data;
+	u8 events[IRQ_NUM];
+	int error;
+
+	/* Check what events have happened */
+	error = regmap_bulk_read(haptics->regmap,
+				 DA7280_IRQ_EVENT1,
+				 events, IRQ_NUM);
+	if (error)
+		goto error_i2c;
+
+	/* Empty check due to shared interrupt */
+	if ((events[0] | events[1] | events[2]) == 0x00)
+		return IRQ_HANDLED;
+
+	if (events[0] & DA7280_E_SEQ_FAULT_MASK) {
+		/* Stop first if Haptic is working
+		 * Otherwise, the fault may happen continually
+		 * even though the bit is cleared.
+		 */
+		error = regmap_update_bits(haptics->regmap,
+					   DA7280_TOP_CTL1,
+					   DA7280_OPERATION_MODE_MASK, 0);
+		if (error)
+			goto error_i2c;
+	}
+
+	/* Clear events */
+	error = regmap_write(haptics->regmap,
+			     DA7280_IRQ_EVENT1, events[0]);
+	if (error)
+		goto error_i2c;
+
+	return IRQ_HANDLED;
+
+error_i2c:
+	dev_err(haptics->dev, "da7280 i2c error : %d\n", error);
+	return IRQ_NONE;
+}
+
+static int da7280_init(struct da7280_haptic *haptics)
+{
+	int error, i;
+	unsigned int val = 0;
+	u32 v2i_factor;
+	u8 mask = 0;
+
+	/* If device type is DA7280_DEV_MAX,
+	 * then just use default value inside chip.
+	 */
+	if (haptics->dev_type == DA7280_DEV_MAX) {
+		error = regmap_read(haptics->regmap, DA7280_TOP_CFG1, &val);
+		if (error)
+			goto error_i2c;
+		if (val & DA7280_ACTUATOR_TYPE_MASK)
+			haptics->dev_type = DA7280_ERM_COIN;
+		else
+			haptics->dev_type = DA7280_LRA;
+	}
+
+	/* Apply user settings */
+	if (haptics->dev_type == DA7280_LRA) {
+		if (haptics->resonant_freq_l != DA7280_SKIP_INIT) {
+			error = regmap_write(haptics->regmap,
+					     DA7280_FRQ_LRA_PER_H,
+					     haptics->resonant_freq_h);
+			if (error)
+				goto error_i2c;
+			error = regmap_write(haptics->regmap,
+					     DA7280_FRQ_LRA_PER_L,
+					     haptics->resonant_freq_l);
+			if (error)
+				goto error_i2c;
+		}
+	} else if (haptics->dev_type == DA7280_ERM_COIN) {
+		error = regmap_update_bits(haptics->regmap,
+					   DA7280_TOP_INT_CFG1,
+					   DA7280_BEMF_FAULT_LIM_MASK, 0);
+		if (error)
+			goto error_i2c;
+
+		mask = DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+			DA7280_V2I_FACTOR_FREEZE_MASK;
+		val = DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+			DA7280_V2I_FACTOR_FREEZE_MASK;
+		error = regmap_update_bits(haptics->regmap,
+					   DA7280_TOP_CFG4,
+					   mask, val);
+		if (error)
+			goto error_i2c;
+
+		haptics->acc_en = false;
+		haptics->rapid_stop_en = false;
+		haptics->amp_pid_en = false;
+	}
+
+	/* Should be set to 0 only
+	 * in custom waveform and wideband operation
+	 */
+	if (haptics->op_mode >= DA7280_RTWM_MODE)
+		haptics->bemf_sense_en = false;
+
+	mask = DA7280_ACTUATOR_TYPE_MASK |
+			DA7280_BEMF_SENSE_EN_MASK |
+			DA7280_FREQ_TRACK_EN_MASK |
+			DA7280_ACCELERATION_EN_MASK |
+			DA7280_RAPID_STOP_EN_MASK |
+			DA7280_AMP_PID_EN_MASK;
+
+	val = (haptics->dev_type ? 1 : 0)
+			<< DA7280_ACTUATOR_TYPE_SHIFT |
+		(haptics->bemf_sense_en ? 1 : 0)
+			<< DA7280_BEMF_SENSE_EN_SHIFT |
+		(haptics->freq_track_en ? 1 : 0)
+			<< DA7280_FREQ_TRACK_EN_SHIFT |
+		(haptics->acc_en ? 1 : 0)
+			<< DA7280_ACCELERATION_EN_SHIFT |
+		(haptics->rapid_stop_en ? 1 : 0)
+			<< DA7280_RAPID_STOP_EN_SHIFT |
+		(haptics->amp_pid_en ? 1 : 0)
+			<< DA7280_AMP_PID_EN_SHIFT;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CFG1, mask, val);
+	if (error)
+		goto error_i2c;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CFG5,
+				   DA7280_V2I_FACTOR_OFFSET_EN_MASK,
+				   haptics->acc_en ?
+				   DA7280_V2I_FACTOR_OFFSET_EN_MASK : 0);
+	if (error)
+		goto error_i2c;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CFG2,
+				   DA7280_MEM_DATA_SIGNED_MASK,
+				   haptics->acc_en ?
+				   0 : DA7280_MEM_DATA_SIGNED_MASK);
+	if (error)
+		goto error_i2c;
+
+	if (haptics->nommax != DA7280_SKIP_INIT) {
+		error = regmap_write(haptics->regmap,
+				     DA7280_ACTUATOR1,
+				     haptics->nommax);
+		if (error)
+			goto error_i2c;
+	}
+
+	if (haptics->absmax != DA7280_SKIP_INIT) {
+		error = regmap_write(haptics->regmap, DA7280_ACTUATOR2,
+				     haptics->absmax);
+		if (error)
+			goto error_i2c;
+	}
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_ACTUATOR3,
+				   DA7280_IMAX_MASK,
+				   haptics->imax);
+	if (error)
+		goto error_i2c;
+
+	v2i_factor =
+		haptics->impd * (haptics->imax + 4) / 1610400;
+	error = regmap_write(haptics->regmap,
+			     DA7280_CALIB_V2I_L,
+			     v2i_factor & 0xff);
+	if (error)
+		goto error_i2c;
+	error = regmap_write(haptics->regmap,
+			     DA7280_CALIB_V2I_H,
+			     v2i_factor >> 8);
+	if (error)
+		goto error_i2c;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_STANDBY_EN_MASK,
+				   DA7280_STANDBY_EN_MASK);
+	if (error)
+		goto error_i2c;
+
+	if (haptics->mem_update) {
+		error = da7280_haptic_mem_update(haptics);
+		if (error)
+			goto error_i2c;
+	}
+
+	/* Set  PS_SEQ_ID and PS_SEQ_LOOP */
+	val = haptics->ps_seq_id << DA7280_PS_SEQ_ID_SHIFT |
+		haptics->ps_seq_loop << DA7280_PS_SEQ_LOOP_SHIFT;
+	error = regmap_write(haptics->regmap,
+			     DA7280_SEQ_CTL2, val);
+	if (error)
+		goto error_i2c;
+
+	/* GPI(N) CTL */
+	for (i = 0; i < 3; i++) {
+		val = haptics->gpi_ctl[i].seq_id
+				<< DA7280_GPI0_SEQUENCE_ID_SHIFT |
+			haptics->gpi_ctl[i].mode
+				<< DA7280_GPI0_MODE_SHIFT |
+			haptics->gpi_ctl[i].polarity
+				<< DA7280_GPI0_POLARITY_SHIFT;
+		error = regmap_write(haptics->regmap,
+				     DA7280_GPI_0_CTL + i, val);
+		if (error)
+			goto error_i2c;
+	}
+
+	/* Clear Interrupts */
+	error = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, 0xff);
+	if (error)
+		goto error_i2c;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_IRQ_MASK1,
+				   DA7280_SEQ_FAULT_M_MASK
+				   | DA7280_SEQ_DONE_M_MASK, 0);
+	if (error)
+		goto error_i2c;
+
+	haptics->enabled = false;
+	return 0;
+
+error_i2c:
+	dev_err(haptics->dev, "haptic init - I2C error : %d\n", error);
+	return error;
+}
+
+/* Valid format for ps_seq_id
+ * echo X > ps_seq_id
+ * ex) echo 2 > /sys/class/..../ps_seq_id
+ * 0 <= X <= 15.
+ */
+static ssize_t ps_seq_id_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf,
+			       size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	long val = 0xff;
+	int error;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_SEQ_CTL2,
+				   DA7280_PS_SEQ_ID_MASK,
+				   (val & 0xf) >> DA7280_PS_SEQ_ID_SHIFT);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", error);
+		return error;
+	}
+
+	haptics->ps_seq_id = val & 0xf;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid input\n");
+	return count;
+}
+
+static ssize_t ps_seq_id_show(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error;
+	unsigned int val;
+
+	error = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", error);
+		return error;
+	}
+	val = (val & DA7280_PS_SEQ_ID_MASK)
+		>> DA7280_PS_SEQ_ID_SHIFT;
+
+	return sprintf(buf, "ps_seq_id is %d\n", val);
+}
+
+/* Valid format for ps_seq_loop
+ * echo X > ps_seq_loop
+ * ex) echo 2 > /sys/class/..../ps_seq_loop
+ * 0 <= X <= 15.
+ */
+static ssize_t ps_seq_loop_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	long val = 0xff;
+	int error;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_SEQ_CTL2,
+				   DA7280_PS_SEQ_LOOP_MASK,
+				   (val & 0xF) << DA7280_PS_SEQ_LOOP_SHIFT);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", error);
+		return error;
+	}
+
+	haptics->ps_seq_loop = (val & 0xF);
+
+	return count;
+err:
+	dev_err(dev, "Invalid input value!\n");
+	return count;
+}
+
+static ssize_t ps_seq_loop_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error;
+	unsigned int val;
+
+	error = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", error);
+		return error;
+	}
+	val = (val & DA7280_PS_SEQ_LOOP_MASK)
+				>> DA7280_PS_SEQ_LOOP_SHIFT;
+
+	return sprintf(buf, "ps_seq_loop is %d\n", val);
+}
+
+/* Valid format for GPIx_SEQUENCE_ID
+ * echo X > ./gpi_seq_id0
+ * Range of X: 0 <= X <= 15
+ * ex)
+ *	echo 1 > /sys/class/..../gpi_seq_id0
+ *	echo 2 > /sys/class/..../gpi_seq_id1
+ *	echo 3 > /sys/class/..../gpi_seq_id2
+ */
+static ssize_t gpi_seq_id0_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	long val = 0xff;
+	int error;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_GPI_0_CTL,
+				   DA7280_GPI0_SEQUENCE_ID_MASK,
+				   (val & 0xf)
+				   << DA7280_GPI0_SEQUENCE_ID_SHIFT);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", error);
+		return error;
+	}
+
+	haptics->gpi_ctl[0].seq_id = val & 0xf;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid input\n");
+	return count;
+}
+
+static ssize_t gpi_seq_id0_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error;
+	unsigned int val;
+
+	error = regmap_read(haptics->regmap, DA7280_GPI_0_CTL, &val);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", error);
+		return error;
+	}
+	val = (val & DA7280_GPI0_SEQUENCE_ID_MASK)
+		>> DA7280_GPI0_SEQUENCE_ID_SHIFT;
+
+	return sprintf(buf, "gpi_seq_id0 is %d\n", val);
+}
+
+static ssize_t gpi_seq_id1_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	long val = 0xff;
+	int error;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_GPI_1_CTL,
+				   DA7280_GPI1_SEQUENCE_ID_MASK,
+				   (val & 0xf)
+				   << DA7280_GPI1_SEQUENCE_ID_SHIFT);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", error);
+		return error;
+	}
+
+	haptics->gpi_ctl[1].seq_id = val & 0xf;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid input\n");
+	return count;
+}
+
+static ssize_t gpi_seq_id1_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error;
+	unsigned int val;
+
+	error = regmap_read(haptics->regmap, DA7280_GPI_1_CTL, &val);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", error);
+		return error;
+	}
+	val = (val & DA7280_GPI1_SEQUENCE_ID_MASK)
+		>> DA7280_GPI1_SEQUENCE_ID_SHIFT;
+
+	return sprintf(buf, "gpi_seq_id1 is %d\n", val);
+}
+
+static ssize_t gpi_seq_id2_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	long val = 0xff;
+	int error;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_GPI_2_CTL,
+				   DA7280_GPI2_SEQUENCE_ID_MASK,
+				   (val & 0xf)
+				   << DA7280_GPI2_SEQUENCE_ID_SHIFT);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", error);
+		return error;
+	}
+
+	haptics->gpi_ctl[2].seq_id = val & 0xf;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid input\n");
+	return count;
+}
+
+static ssize_t gpi_seq_id2_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error;
+	unsigned int val;
+
+	error = regmap_read(haptics->regmap, DA7280_GPI_2_CTL, &val);
+	if (error) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", error);
+		return error;
+	}
+	val = (val & DA7280_GPI2_SEQUENCE_ID_MASK)
+		>> DA7280_GPI2_SEQUENCE_ID_SHIFT;
+
+	return sprintf(buf, "gpi_seq_id2 is %d\n", val);
+}
+
+#define MAX_PTN_REGS DA7280_SNP_MEM_SIZE
+#define MAX_USER_INPUT_LEN (5 * DA7280_SNP_MEM_SIZE)
+struct parse_data_t {
+	int len;
+	u8 val[MAX_PTN_REGS];
+};
+
+static int da7280_parse_args(struct device *dev,
+			     char *cmd, struct parse_data_t *ptn)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	char *tok;		/* used to separate tokens */
+	const char ct[] = " \t"; /* space or tab delimits the tokens */
+	int tok_count = 0;	/* total number of tokens parsed */
+	int i = 0, val;
+
+	ptn->len = 0;
+
+	/* parse the input string */
+	while ((tok = strsep(&cmd, ct)) != NULL) {
+		/* this is a value to be written to the register */
+		if (kstrtouint(tok, 0, &val) < 0) {
+			dev_err(haptics->dev,
+				"failed to read from %s\n", tok);
+			break;
+		}
+
+		if (i < MAX_PTN_REGS) {
+			ptn->val[i] = val;
+			i++;
+		}
+		tok_count++;
+	}
+
+	/* decide whether it is a read or write operation based on the
+	 * value of tok_count and count_flag.
+	 * tok_count = 0: no inputs, invalid case.
+	 * tok_count = 1: write one value.
+	 * tok_count > 1: write multiple values/patterns.
+	 */
+	switch (tok_count) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		ptn->len = 1;
+		break;
+	default:
+		ptn->len = i;
+	}
+	return 0;
+}
+
+static ssize_t
+patterns_store(struct device *dev,
+	       struct device_attribute *attr,
+	       const char *buf,
+	       size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	struct parse_data_t mem;
+	char cmd[MAX_USER_INPUT_LEN];
+	unsigned int val;
+	int error;
+
+	error = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
+	if (error)
+		return error;
+
+	if (count > MAX_USER_INPUT_LEN)
+		memcpy(cmd, buf, MAX_USER_INPUT_LEN);
+	else
+		memcpy(cmd, buf, count);
+
+	/* chop of '\n' introduced by echo at the end of the input */
+	if (cmd[count - 1] == '\n')
+		cmd[count - 1] = '\0';
+
+	if (da7280_parse_args(dev, cmd, &mem) < 0)
+		return -EINVAL;
+
+	memcpy(haptics->snp_mem, mem.val, mem.len);
+
+	error = da7280_haptic_mem_update(haptics);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(ps_seq_id);
+static DEVICE_ATTR_RW(ps_seq_loop);
+static DEVICE_ATTR_RW(gpi_seq_id0);
+static DEVICE_ATTR_RW(gpi_seq_id1);
+static DEVICE_ATTR_RW(gpi_seq_id2);
+static DEVICE_ATTR_WO(patterns);
+static struct attribute *da7280_sysfs_attr[] = {
+	&dev_attr_ps_seq_id.attr,
+	&dev_attr_ps_seq_loop.attr,
+	&dev_attr_gpi_seq_id0.attr,
+	&dev_attr_gpi_seq_id1.attr,
+	&dev_attr_gpi_seq_id2.attr,
+	&dev_attr_patterns.attr,
+	NULL,
+};
+
+static const struct attribute_group da7280_attr_group = {
+	.attrs = da7280_sysfs_attr,
+};
+
+static int da7280_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct da7280_haptic *haptics;
+	unsigned int period2freq;
+	int error;
+
+	haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+	haptics->dev = dev;
+
+	if (!client->irq) {
+		dev_err(dev, "No IRQ configured\n");
+		return -EINVAL;
+	}
+
+	da7280_parse_properties(&client->dev, haptics);
+
+	if (haptics->op_mode == DA7280_PWM_MODE) {
+		/* Get pwm and regulatot for haptics device */
+		haptics->pwm_dev = devm_pwm_get(&client->dev, NULL);
+		if (IS_ERR(haptics->pwm_dev)) {
+			dev_err(dev, "failed to get PWM device\n");
+			return PTR_ERR(haptics->pwm_dev);
+		}
+
+		/*
+		 * FIXME: pwm_apply_args() should be removed when switching to
+		 * the atomic PWM API.
+		 */
+		pwm_apply_args(haptics->pwm_dev);
+
+		/* Check PWM Period, it must be in 10k ~ 250kHz */
+		period2freq = 1000000 / pwm_get_period(haptics->pwm_dev);
+		if (period2freq < DA7280_MIN_PWM_FREQ_KHZ ||
+		    period2freq > DA7280_MAX_PWM_FREQ_KHZ) {
+			dev_err(dev, "Not supported PWM frequency(%d)\n",
+				period2freq);
+			return -EINVAL;
+		}
+	}
+
+	INIT_WORK(&haptics->work, da7280_haptic_work);
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap =
+		devm_regmap_init_i2c(client, &da7280_haptic_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		error = PTR_ERR(haptics->regmap);
+		dev_err(dev, "Failed to allocate register map : %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(dev, client->irq, NULL,
+					  da7280_irq_handler,
+					  IRQF_ONESHOT,
+					  "da7280-haptics", haptics);
+	if (error != 0) {
+		dev_err(dev,
+			"Failed to request IRQ : %d\n", client->irq);
+		return error;
+	}
+
+	error = da7280_init(haptics);
+	if (error) {
+		dev_err(dev, "failed to initialize device\n");
+		return error;
+	}
+
+	/* Initialize input device for haptic device */
+	haptics->input_dev = devm_input_allocate_device(dev);
+	if (!haptics->input_dev) {
+		dev_err(dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	haptics->input_dev->name = "da7280-haptic";
+	haptics->input_dev->dev.parent = client->dev.parent;
+	haptics->input_dev->open = da7280_haptic_open;
+	haptics->input_dev->close = da7280_haptic_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(haptics->input_dev, NULL,
+					da7280_haptic_play);
+	if (error) {
+		dev_err(dev, "failed to create force-feedback\n");
+		return error;
+	}
+
+	error = input_register_device(haptics->input_dev);
+	if (error) {
+		dev_err(dev, "failed to register input device\n");
+		return error;
+	}
+
+	error = devm_device_add_group(dev, &da7280_attr_group);
+	if (error) {
+		dev_err(dev, "Failed to create sysfs attributes: %d\n",
+			error);
+	}
+
+	return error;
+}
+
+static int __maybe_unused da7280_suspend(struct device *dev)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	da7280_haptic_disable(haptics);
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_STANDBY_EN_MASK, 0);
+	if (error)
+		dev_err(haptics->dev,
+			"I2C error : %d\n", error);
+
+	mutex_unlock(&haptics->input_dev->mutex);
+	return error;
+}
+
+static int __maybe_unused da7280_resume(struct device *dev)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int error = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	error = regmap_update_bits(haptics->regmap,
+				   DA7280_TOP_CTL1,
+				   DA7280_STANDBY_EN_MASK,
+				   DA7280_STANDBY_EN_MASK);
+	if (error)
+		dev_err(haptics->dev,
+			"i2c error : %d\n", error);
+
+	mutex_unlock(&haptics->input_dev->mutex);
+	return error;
+}
+
+static const struct of_device_id da7280_of_match[] = {
+	{ .compatible = "dlg,da7280", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, da7280_of_match);
+
+static const struct i2c_device_id da7280_i2c_id[] = {
+	{ "da7280", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, da7280_i2c_id);
+
+static SIMPLE_DEV_PM_OPS(da7280_pm_ops,
+		 da7280_suspend, da7280_resume);
+
+static struct i2c_driver da7280_driver = {
+	.driver		= {
+		.name	= "da7280",
+		.of_match_table = of_match_ptr(da7280_of_match),
+		.pm	= &da7280_pm_ops,
+	},
+	.probe	= da7280_probe,
+	.id_table	= da7280_i2c_id,
+};
+module_i2c_driver(da7280_driver);
+
+MODULE_DESCRIPTION("DA7280 haptics driver");
+MODULE_AUTHOR("Roy Im <Roy.Im.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/da7280.h b/drivers/input/misc/da7280.h
new file mode 100644
index 0000000..d9310b6
--- /dev/null
+++ b/drivers/input/misc/da7280.h
@@ -0,0 +1,412 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * DA7280 Haptic device driver registers
+ *
+ * Copyright (c) 2017 Dialog Semiconductor.
+ * Author: Roy Im <Roy.Im.Opensource@diasemi.com>
+ */
+
+#ifndef _DA7280_REG_DEFS_H
+#define _DA7280_REG_DEFS_H
+
+#include <linux/bitops.h>
+
+/* Registers */
+
+#define DA7280_CHIP_REV                       0x00
+#define DA7280_IRQ_EVENT1                     0x03
+#define DA7280_IRQ_EVENT_WARNING_DIAG         0x04
+#define DA7280_IRQ_EVENT_SEQ_DIAG             0x05
+#define DA7280_IRQ_STATUS1                    0x06
+#define DA7280_IRQ_MASK1                      0x07
+#define DA7280_CIF_I2C1                       0x08
+#define DA7280_FRQ_LRA_PER_H                  0x0A
+#define DA7280_FRQ_LRA_PER_L                  0x0B
+#define DA7280_ACTUATOR1                      0x0C
+#define DA7280_ACTUATOR2                      0x0D
+#define DA7280_ACTUATOR3                      0x0E
+#define DA7280_CALIB_V2I_H                    0x0F
+#define DA7280_CALIB_V2I_L                    0x10
+#define DA7280_CALIB_IMP_H                    0x11
+#define DA7280_CALIB_IMP_L                    0x12
+#define DA7280_TOP_CFG1                       0x13
+#define DA7280_TOP_CFG2                       0x14
+#define DA7280_TOP_CFG3                       0x15
+#define DA7280_TOP_CFG4                       0x16
+#define DA7280_TOP_INT_CFG1                   0x17
+#define DA7280_TOP_INT_CFG6_H                 0x1C
+#define DA7280_TOP_INT_CFG6_L                 0x1D
+#define DA7280_TOP_INT_CFG7_H                 0x1E
+#define DA7280_TOP_INT_CFG7_L                 0x1F
+#define DA7280_TOP_INT_CFG8                   0x20
+#define DA7280_TOP_CTL1                       0x22
+#define DA7280_TOP_CTL2                       0x23
+#define DA7280_SEQ_CTL1                       0x24
+#define DA7280_SWG_C1                         0x25
+#define DA7280_SWG_C2                         0x26
+#define DA7280_SWG_C3                         0x27
+#define DA7280_SEQ_CTL2                       0x28
+#define DA7280_GPI_0_CTL                      0x29
+#define DA7280_GPI_1_CTL                      0x2A
+#define DA7280_GPI_2_CTL                      0x2B
+#define DA7280_MEM_CTL1                       0x2C
+#define DA7280_MEM_CTL2                       0x2D
+#define DA7280_ADC_DATA_H1                    0x2E
+#define DA7280_ADC_DATA_L1                    0x2F
+#define DA7280_POLARITY                       0x43
+#define DA7280_LRA_AVR_H                      0x44
+#define DA7280_LRA_AVR_L                      0x45
+#define DA7280_FRQ_LRA_PER_ACT_H              0x46
+#define DA7280_FRQ_LRA_PER_ACT_L              0x47
+#define DA7280_FRQ_PHASE_H                    0x48
+#define DA7280_FRQ_PHASE_L                    0x49
+#define DA7280_FRQ_CTL                        0x4C
+#define DA7280_TRIM3                          0x5F
+#define DA7280_TRIM4                          0x60
+#define DA7280_TRIM6                          0x62
+#define DA7280_TOP_CFG5                       0x6E
+#define DA7280_IRQ_EVENT_ACTUATOR_FAULT       0x81
+#define DA7280_IRQ_STATUS2                    0x82
+#define DA7280_IRQ_MASK2                      0x83
+#define DA7280_SNP_MEM_0                      0x84
+#define DA7280_SNP_MEM_99                     0xE7
+
+/* DA7280_CHIP_REV (Address 0x00) */
+#define DA7280_CHIP_REV_MAJOR_SHIFT		0
+#define DA7280_CHIP_REV_MAJOR_MASK		(15 << 0)
+#define DA7280_CHIP_REV_MINOR_SHIFT		4
+#define DA7280_CHIP_REV_MINOR_MASK		(15 << 4)
+
+/* DA7280_IRQ_EVENT1 (Address 0x03) */
+#define DA7280_E_SEQ_CONTINUE_SHIFT		0
+#define DA7280_E_SEQ_CONTINUE_MASK		BIT(0)
+#define DA7280_E_UVLO_SHIFT			1
+#define DA7280_E_UVLO_MASK			BIT(1)
+#define DA7280_E_SEQ_DONE_SHIFT			2
+#define DA7280_E_SEQ_DONE_MASK			BIT(2)
+#define DA7280_E_OVERTEMP_CRIT_SHIFT		3
+#define DA7280_E_OVERTEMP_CRIT_MASK		BIT(3)
+#define DA7280_E_SEQ_FAULT_SHIFT		4
+#define DA7280_E_SEQ_FAULT_MASK			BIT(4)
+#define DA7280_E_WARNING_SHIFT			5
+#define DA7280_E_WARNING_MASK			BIT(5)
+#define DA7280_E_ACTUATOR_FAULT_SHIFT		6
+#define DA7280_E_ACTUATOR_FAULT_MASK		BIT(6)
+#define DA7280_E_OC_FAULT_SHIFT			7
+#define DA7280_E_OC_FAULT_MASK			BIT(7)
+
+/* DA7280_IRQ_EVENT_WARNING_DIAG (Address 0x04) */
+#define DA7280_E_OVERTEMP_WARN_SHIFT            3
+#define DA7280_E_OVERTEMP_WARN_MASK             BIT(3)
+#define DA7280_E_MEM_TYPE_SHIFT                 4
+#define DA7280_E_MEM_TYPE_MASK                  BIT(4)
+#define DA7280_E_LIM_DRIVE_ACC_SHIFT            6
+#define DA7280_E_LIM_DRIVE_ACC_MASK             BIT(6)
+#define DA7280_E_LIM_DRIVE_SHIFT                7
+#define DA7280_E_LIM_DRIVE_MASK                 BIT(7)
+
+/* DA7280_IRQ_EVENT_PAT_DIAG (Address 0x05) */
+#define DA7280_E_PWM_FAULT_SHIFT		5
+#define DA7280_E_PWM_FAULT_MASK			BIT(5)
+#define DA7280_E_MEM_FAULT_SHIFT		6
+#define DA7280_E_MEM_FAULT_MASK			BIT(6)
+#define DA7280_E_SEQ_ID_FAULT_SHIFT		7
+#define DA7280_E_SEQ_ID_FAULT_MASK		BIT(7)
+
+/* DA7280_IRQ_STATUS1 (Address 0x06) */
+#define DA7280_STA_SEQ_CONTINUE_SHIFT		0
+#define DA7280_STA_SEQ_CONTINUE_MASK		BIT(0)
+#define DA7280_STA_UVLO_VBAT_OK_SHIFT		1
+#define DA7280_STA_UVLO_VBAT_OK_MASK		BIT(1)
+#define DA7280_STA_SEQ_DONE_SHIFT		2
+#define DA7280_STA_SEQ_DONE_MASK		BIT(2)
+#define DA7280_STA_OVERTEMP_CRIT_SHIFT		3
+#define DA7280_STA_OVERTEMP_CRIT_MASK		BIT(3)
+#define DA7280_STA_SEQ_FAULT_SHIFT		4
+#define DA7280_STA_SEQ_FAULT_MASK		BIT(4)
+#define DA7280_STA_WARNING_SHIFT		5
+#define DA7280_STA_WARNING_MASK			BIT(5)
+#define DA7280_STA_ACTUATOR_SHIFT		6
+#define DA7280_STA_ACTUATOR_MASK		BIT(6)
+#define DA7280_STA_OC_SHIFT			7
+#define DA7280_STA_OC_MASK			BIT(7)
+
+/* DA7280_IRQ_MASK1 (Address 0x07) */
+#define DA7280_SEQ_CONTINUE_M_SHIFT		0
+#define DA7280_SEQ_CONTINUE_M_MASK		BIT(0)
+#define DA7280_E_UVLO_M_SHIFT			1
+#define DA7280_E_UVLO_M_MASK			BIT(1)
+#define DA7280_SEQ_DONE_M_SHIFT			2
+#define DA7280_SEQ_DONE_M_MASK			BIT(2)
+#define DA7280_OVERTEMP_CRIT_M_SHIFT		3
+#define DA7280_OVERTEMP_CRIT_M_MASK		BIT(3)
+#define DA7280_SEQ_FAULT_M_SHIFT		4
+#define DA7280_SEQ_FAULT_M_MASK			BIT(4)
+#define DA7280_WARNING_M_SHIFT			5
+#define DA7280_WARNING_M_MASK			BIT(5)
+#define DA7280_ACTUATOR_M_SHIFT			6
+#define DA7280_ACTUATOR_M_MASK			BIT(6)
+#define DA7280_OC_M_SHIFT			7
+#define DA7280_OC_M_MASK			BIT(7)
+
+/* DA7280_CIF_I2C1 (Address 0x08) */
+#define DA7280_I2C_TO_ENABLE_SHIFT		6
+#define DA7280_I2C_TO_ENABLE_MASK		BIT(6)
+#define DA7280_I2C_WR_MODE_SHIFT		7
+#define DA7280_I2C_WR_MODE_MASK			BIT(7)
+
+/* DA7280_FRQ_LRA_PER_H (Address 0x0a) */
+#define DA7280_LRA_PER_H_SHIFT			0
+#define DA7280_LRA_PER_H_MASK			(255 << 0)
+
+/* DA7280_FRQ_LRA_PER_L (Address 0x0b) */
+#define DA7280_LRA_PER_L_SHIFT			0
+#define DA7280_LRA_PER_L_MASK			(127 << 0)
+
+/* DA7280_ACTUATOR1 (Address 0x0c) */
+#define DA7280_ACTUATOR_NOMMAX_SHIFT		0
+#define DA7280_ACTUATOR_NOMMAX_MASK		(255 << 0)
+
+/* DA7280_ACTUATOR2 (Address 0x0d) */
+#define DA7280_ACTUATOR_ABSMAX_SHIFT		0
+#define DA7280_ACTUATOR_ABSMAX_MASK		(255 << 0)
+
+/* DA7280_ACTUATOR3 (Address 0x0e) */
+#define DA7280_IMAX_SHIFT			0
+#define DA7280_IMAX_MASK			(31 << 0)
+
+/* DA7280_CALIB_V2I_H (Address 0x0f) */
+#define DA7280_V2I_FACTOR_H_SHIFT		0
+#define DA7280_V2I_FACTOR_H_MASK		(255 << 0)
+
+/* DA7280_CALIB_V2I_L (Address 0x10) */
+#define DA7280_V2I_FACTOR_L_SHIFT		0
+#define DA7280_V2I_FACTOR_L_MASK		(255 << 0)
+
+/* DA7280_CALIB_IMP_H (Address 0x11) */
+#define DA7280_IMPEDANCE_H_SHIFT		0
+#define DA7280_IMPEDANCE_H_MASK			(255 << 0)
+
+/* DA7280_CALIB_IMP_L (Address 0x12) */
+#define DA7280_IMPEDANCE_L_SHIFT		0
+#define DA7280_IMPEDANCE_L_MASK			(3 << 0)
+
+/* DA7280_TOP_CFG1 (Address 0x13) */
+#define DA7280_AMP_PID_EN_SHIFT			0
+#define DA7280_AMP_PID_EN_MASK			BIT(0)
+#define DA7280_RAPID_STOP_EN_SHIFT		1
+#define DA7280_RAPID_STOP_EN_MASK		BIT(1)
+#define DA7280_ACCELERATION_EN_SHIFT		2
+#define DA7280_ACCELERATION_EN_MASK		BIT(2)
+#define DA7280_FREQ_TRACK_EN_SHIFT		3
+#define DA7280_FREQ_TRACK_EN_MASK		BIT(3)
+#define DA7280_BEMF_SENSE_EN_SHIFT		 4
+#define DA7280_BEMF_SENSE_EN_MASK		BIT(4)
+#define DA7280_ACTUATOR_TYPE_SHIFT		5
+#define DA7280_ACTUATOR_TYPE_MASK		BIT(5)
+#define DA7280_EMBEDDED_MODE_SHIFT		7
+#define DA7280_EMBEDDED_MODE_MASK		BIT(7)
+
+/* DA7280_TOP_CFG2 (Address 0x14) */
+#define DA7280_FULL_BRAKE_THR_SHIFT		0
+#define DA7280_FULL_BRAKE_THR_MASK		(15 << 0)
+#define DA7280_MEM_DATA_SIGNED_SHIFT		4
+#define DA7280_MEM_DATA_SIGNED_MASK		BIT(4)
+
+/* DA7280_TOP_CFG3 (Address 0x15) */
+#define DA7280_VDD_MARGIN_SHIFT			0
+#define DA7280_VDD_MARGIN_MASK			(15 << 0)
+
+/* DA7280_TOP_CFG4 (Address 0x16) */
+#define DA7280_TST_CALIB_IMPEDANCE_DIS_SHIFT	6
+#define DA7280_TST_CALIB_IMPEDANCE_DIS_MASK	BIT(6)
+#define DA7280_V2I_FACTOR_FREEZE_SHIFT		7
+#define DA7280_V2I_FACTOR_FREEZE_MASK		BIT(7)
+
+/* DA7280_TOP_INT_CFG1 (Address 0x17) */
+#define DA7280_BEMF_FAULT_LIM_SHIFT		0
+#define DA7280_BEMF_FAULT_LIM_MASK		(3 << 0)
+#define DA7280_FRQ_LOCKED_LIM_SHIFT		2
+#define DA7280_FRQ_LOCKED_LIM_MASK		(63 << 2)
+
+/* DA7280_TOP_INT_CFG6_H (Address 0x1c) */
+#define DA7280_FRQ_PID_KP_H_SHIFT		0
+#define DA7280_FRQ_PID_KP_H_MASK		(255 << 0)
+
+/* DA7280_TOP_INT_CFG6_L (Address 0x1d) */
+#define DA7280_FRQ_PID_KP_L_SHIFT		0
+#define DA7280_FRQ_PID_KP_L_MASK		(255 << 0)
+
+/* DA7280_TOP_INT_CFG7_H (Address 0x1e) */
+#define DA7280_FRQ_PID_KI_H_SHIFT		0
+#define DA7280_FRQ_PID_KI_H_MASK		(255 << 0)
+
+/* DA7280_TOP_INT_CFG7_L (Address 0x1f) */
+#define DA7280_FRQ_PID_KI_L_SHIFT		0
+#define DA7280_FRQ_PID_KI_L_MASK		(255 << 0)
+
+/* DA7280_TOP_INT_CFG8 (Address 0x20) */
+#define DA7280_TST_FRQ_TRACK_BEMF_LIM_SHIFT     0
+#define DA7280_TST_FRQ_TRACK_BEMF_LIM_MASK      (15 << 0)
+#define DA7280_TST_AMP_RAPID_STOP_LIM_SHIFT     4
+#define DA7280_TST_AMP_RAPID_STOP_LIM_MASK      (7 << 4)
+
+/* DA7280_TOP_CTL1 (Address 0x22) */
+#define DA7280_OPERATION_MODE_SHIFT		0
+#define DA7280_OPERATION_MODE_MASK		(7 << 0)
+#define DA7280_STANDBY_EN_SHIFT			3
+#define DA7280_STANDBY_EN_MASK			BIT(3)
+#define DA7280_SEQ_START_SHIFT			4
+#define DA7280_SEQ_START_MASK			BIT(4)
+
+/* DA7280_TOP_CTL2 (Address 0x23) */
+#define DA7280_OVERRIDE_VAL_SHIFT		0
+#define DA7280_OVERRIDE_VAL_MASK		(255 << 0)
+
+/* DA7280_SEQ_CTL1 (Address 0x24) */
+#define DA7280_SEQ_CONTINUE_SHIFT		0
+#define DA7280_SEQ_CONTINUE_MASK		BIT(0)
+#define DA7280_WAVEGEN_MODE_SHIFT		1
+#define DA7280_WAVEGEN_MODE_MASK		BIT(1)
+#define DA7280_FREQ_WAVEFORM_TIMEBASE_SHIFT	2
+#define DA7280_FREQ_WAVEFORM_TIMEBASE_MASK	BIT(2)
+
+/* DA7280_SWG_C1 (Address 0x25) */
+#define DA7280_CUSTOM_WAVE_GEN_COEFF1_SHIFT	0
+#define DA7280_CUSTOM_WAVE_GEN_COEFF1_MASK	(255 << 0)
+
+/* DA7280_SWG_C2 (Address 0x26) */
+#define DA7280_CUSTOM_WAVE_GEN_COEFF2_SHIFT	0
+#define DA7280_CUSTOM_WAVE_GEN_COEFF2_MASK	(255 << 0)
+
+/* DA7280_SWG_C3 (Address 0x27) */
+#define DA7280_CUSTOM_WAVE_GEN_COEFF3_SHIFT	0
+#define DA7280_CUSTOM_WAVE_GEN_COEFF3_MASK	(255 << 0)
+
+/* DA7280_SEQ_CTL2 (Address 0x28) */
+#define DA7280_PS_SEQ_ID_SHIFT			0
+#define DA7280_PS_SEQ_ID_MASK			(15 << 0)
+#define DA7280_PS_SEQ_LOOP_SHIFT		4
+#define DA7280_PS_SEQ_LOOP_MASK			(15 << 4)
+
+/* DA7280_GPIO_0_CTL (Address 0x29) */
+#define DA7280_GPI0_POLARITY_SHIFT		0
+#define DA7280_GPI0_POLARITY_MASK		(3 << 0)
+#define DA7280_GPI0_MODE_SHIFT			2
+#define DA7280_GPI0_MODE_MASK			BIT(2)
+#define DA7280_GPI0_SEQUENCE_ID_SHIFT		3
+#define DA7280_GPI0_SEQUENCE_ID_MASK		(15 << 3)
+
+/* DA7280_GPIO_1_CTL (Address 0x2a) */
+#define DA7280_GPI1_POLARITY_SHIFT		0
+#define DA7280_GPI1_POLARITY_MASK		(3 << 0)
+#define DA7280_GPI1_MODE_SHIFT			2
+#define DA7280_GPI1_MODE_MASK			BIT(2)
+#define DA7280_GPI1_SEQUENCE_ID_SHIFT		3
+#define DA7280_GPI1_SEQUENCE_ID_MASK		(15 << 3)
+
+/* DA7280_GPIO_2_CTL (Address 0x2b) */
+#define DA7280_GPI2_POLARITY_SHIFT		0
+#define DA7280_GPI2_POLARITY_MASK		(3 << 0)
+#define DA7280_GPI2_MODE_SHIFT			2
+#define DA7280_GPI2_MODE_MASK			BIT(2)
+#define DA7280_GPI2_SEQUENCE_ID_SHIFT		3
+#define DA7280_GPI2_SEQUENCE_ID_MASK		(15 << 3)
+
+/* DA7280_MEM_CTL1 (Address 0x2c) */
+#define DA7280_WAV_MEM_BASE_ADDR_SHIFT		0
+#define DA7280_WAV_MEM_BASE_ADDR_MASK		(255 << 0)
+
+/* DA7280_MEM_CTL2 (Address 0x2d) */
+#define DA7280_WAV_MEM_LOCK_SHIFT		7
+#define DA7280_WAV_MEM_LOCK_MASK		BIT(7)
+
+/* DA7280_ADC_DATA_H1 (Address 0x2e) */
+#define DA7280_ADC_VDD_H_SHIFT			0
+#define DA7280_ADC_VDD_H_MASK			(255 << 0)
+
+/* DA7280_ADC_DATA_L1 (Address 0x2f) */
+#define DA7280_ADC_VDD_L_SHIFT			0
+#define DA7280_ADC_VDD_L_MASK			(127 << 0)
+
+/* DA7280_POLARITY (Address 0x43) */
+#define DA7280_POLARITY_SHIFT			0
+#define DA7280_POLARITY_MASK			BIT(0)
+
+/* DA7280_LRA_AVR_H (Address 0x44) */
+#define DA7280_LRA_PER_AVERAGE_H_SHIFT		0
+#define DA7280_LRA_PER_AVERAGE_H_MASK		(255 << 0)
+
+/* DA7280_LRA_AVR_L (Address 0x45) */
+#define DA7280_LRA_PER_AVERAGE_L_SHIFT		0
+#define DA7280_LRA_PER_AVERAGE_L_MASK		(127 << 0)
+
+/* DA7280_FRQ_LRA_PER_ACT_H (Address 0x46) */
+#define DA7280_LRA_PER_ACTUAL_H_SHIFT		0
+#define DA7280_LRA_PER_ACTUAL_H_MASK		(255 << 0)
+
+/* DA7280_FRQ_LRA_PER_ACT_L (Address 0x47) */
+#define DA7280_LRA_PER_ACTUAL_L_SHIFT		0
+#define DA7280_LRA_PER_ACTUAL_L_MASK		(127 << 0)
+
+/* DA7280_FRQ_PHASE_H (Address 0x48) */
+#define DA7280_PHASE_DELAY_H_SHIFT		0
+#define DA7280_PHASE_DELAY_H_MASK		(255 << 0)
+
+/* DA7280_FRQ_PHASE_L (Address 0x49) */
+#define DA7280_DELAY_SHIFT_L_SHIFT		0
+#define DA7280_DELAY_SHIFT_L_MASK		(7 << 0)
+#define DA7280_DELAY_SHIFT_FREEZE_SHIFT		7
+#define DA7280_DELAY_SHIFT_FREEZE_MASK		BIT(7)
+
+/* DA7280_FRQ_CTL (Address 0x4c) */
+#define DA7280_FREQ_TRACKING_FORCE_ON_SHIFT	0
+#define DA7280_FREQ_TRACKING_FORCE_ON_MASK	BIT(0)
+#define DA7280_FREQ_TRACKING_AUTO_ADJ_SHIFT	1
+#define DA7280_FREQ_TRACKING_AUTO_ADJ_MASK	BIT(1)
+
+/* DA7280_TRIM3 (Address 0x5f) */
+#define DA7280_REF_UVLO_THRES_SHIFT		3
+#define DA7280_REF_UVLO_THRES_MASK		(3 << 3)
+#define DA7280_LOOP_FILT_LOW_BW_SHIFT		5
+#define DA7280_LOOP_FILT_LOW_BW_MASK		BIT(5)
+#define DA7280_LOOP_IDAC_DOUBLE_RANGE_SHIFT	6
+#define DA7280_LOOP_IDAC_DOUBLE_RANGE_MASK	BIT(6)
+
+/* DA7280_TRIM4 (Address 0x60) */
+#define DA7280_LOOP_FILT_RES_TRIM_SHIFT		0
+#define DA7280_LOOP_FILT_RES_TRIM_MASK		(3 << 0)
+#define DA7280_LOOP_FILT_CAP_TRIM_SHIFT		2
+#define DA7280_LOOP_FILT_CAP_TRIM_MASK		(3 << 2)
+
+/* DA7280_TRIM6 (Address 0x62) */
+#define DA7280_HBRIDGE_ERC_HS_TRIM_SHIFT	0
+#define DA7280_HBRIDGE_ERC_HS_TRIM_MASK		(3 << 0)
+#define DA7280_HBRIDGE_ERC_LS_TRIM_SHIFT	2
+#define DA7280_HBRIDGE_ERC_LS_TRIM_MASK		(3 << 2)
+
+/* DA7280_TOP_CFG5 (Address 0x6e) */
+#define DA7280_V2I_FACTOR_OFFSET_EN_SHIFT		0
+#define DA7280_V2I_FACTOR_OFFSET_EN_MASK		BIT(0)
+#define DA7280_FRQ_PAUSE_ON_POLARITY_CHANGE_SHIFT	1
+#define DA7280_FRQ_PAUSE_ON_POLARITY_CHANGE_MASK	BIT(1)
+#define DA7280_DELAY_BYPASS_SHIFT			2
+#define DA7280_DELAY_BYPASS_MASK			BIT(2)
+
+/* DA7280_IRQ_EVENT_ACTUATOR_FAULT (Address 0x81) */
+#define DA7280_ADC_SAT_FAULT_SHIFT		2
+#define DA7280_ADC_SAT_FAULT_MASK		BIT(2)
+
+/* DA7280_IRQ_STATUS2 (Address 0x82) */
+#define DA7280_STA_ADC_SAT_SHIFT		7
+#define DA7280_STA_ADC_SAT_MASK			BIT(7)
+
+/* DA7280_IRQ_MASK2 (Address 0x83) */
+#define DA7280_ADC_SAT_M_SHIFT			7
+#define DA7280_ADC_SAT_M_MASK			BIT(7)
+
+/* DA7280_SNP_MEM_XX (Address 0x84 ~ 0xe7) */
+#define DA7280_SNP_MEM_SHIFT			0
+#define DA7280_SNP_MEM_MASK			(255 << 0)
+
+#endif
-- 
end-of-patch for RESEND PATCH V7

^ permalink raw reply related

* [PATCH] Revert "HID: add NOGET quirk for Eaton Ellipse MAX UPS"
From: Benjamin Tissoires @ 2018-10-17  7:01 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Laurent Bigonville, linux-input, linux-kernel, Benjamin Tissoires

This reverts commit 67ddbb3e6568fb1820b2cc45b00c50702b114801.

67ddbb3e656 ("HID: add NOGET quirk for Eaton Ellipse MAX UPS") was reported
by Laurent Bigonville. It turns out that a later model Laurent got
doesn't need the quirk after all.

My take is that Eaton upgraded their firmwares, so we don't need it
anymore.

The old model was from 2012, so better make sure the new line works
properly by removing the quirk. This allows upower to actually fetch
the current data.

Reported-by: Laurent Bigonville <bigon@bigon.be>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-quirks.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 279f410618c6..e260bdeb51a7 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -107,7 +107,6 @@ static const struct hid_device_id hid_quirks[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A), HID_QUIRK_ALWAYS_POLL },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A), HID_QUIRK_ALWAYS_POLL },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_GAMEPADBLOCK), HID_QUIRK_MULTI_INPUT },
-	{ HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS), HID_QUIRK_NOGET },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), HID_QUIRK_NO_INIT_REPORTS },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
-- 
2.14.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox