Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH] [v3] HID: add support for Apple Magic Trackpad 2
From: Sean O'Brien @ 2018-09-20 23:13 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Sean O'Brien, Marek Wyborski, linux-kernel, Dmitry Torokhov,
	linux-input, Henrik Rydberg, Jiri Kosina, Claudio Mettler

USB device
        Vendor 05ac (Apple)
        Device 0265 (Magic Trackpad 2)
Bluetooth device
        Vendor 004c (Apple)
        Device 0265 (Magic Trackpad 2)

Add support for Apple Magic Trackpad 2 over USB and bluetooth, putting
the device in multi-touch mode.

Signed-off-by: Claudio Mettler <claudio@ponyfleisch.ch>
Signed-off-by: Marek Wyborski <marek.wyborski@emwesoft.com>
Signed-off-by: Sean O'Brien <seobrien@chromium.org>
---

 drivers/hid/hid-ids.h        |   1 +
 drivers/hid/hid-magicmouse.c | 149 +++++++++++++++++++++++++++++++----
 2 files changed, 134 insertions(+), 16 deletions(-)

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 5146ee029db4..bb0cd212c7cc 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -92,6 +92,7 @@
 #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
 #define USB_DEVICE_ID_APPLE_MAGICMOUSE	0x030d
 #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD	0x030e
+#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2	0x0265
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
 #define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index b454c4386157..6a3a6c83e509 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -54,6 +54,8 @@ module_param(report_undeciphered, bool, 0644);
 MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
 
 #define TRACKPAD_REPORT_ID 0x28
+#define TRACKPAD2_USB_REPORT_ID 0x02
+#define TRACKPAD2_BT_REPORT_ID 0x31
 #define MOUSE_REPORT_ID    0x29
 #define DOUBLE_REPORT_ID   0xf7
 /* These definitions are not precise, but they're close enough.  (Bits
@@ -91,6 +93,17 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
 #define TRACKPAD_RES_Y \
 	((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
 
+#define TRACKPAD2_DIMENSION_X (float)16000
+#define TRACKPAD2_MIN_X -3678
+#define TRACKPAD2_MAX_X 3934
+#define TRACKPAD2_RES_X \
+	((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100))
+#define TRACKPAD2_DIMENSION_Y (float)11490
+#define TRACKPAD2_MIN_Y -2478
+#define TRACKPAD2_MAX_Y 2587
+#define TRACKPAD2_RES_Y \
+	((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
+
 /**
  * struct magicmouse_sc - Tracks Magic Mouse-specific data.
  * @input: Input device through which we report events.
@@ -183,6 +196,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
 {
 	struct input_dev *input = msc->input;
 	int id, x, y, size, orientation, touch_major, touch_minor, state, down;
+	int pressure = 0;
 
 	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
 		id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
@@ -194,6 +208,20 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
 		touch_minor = tdata[4];
 		state = tdata[7] & TOUCH_STATE_MASK;
 		down = state != TOUCH_STATE_NONE;
+	} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		id = tdata[8] & 0xf;
+		x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
+		y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
+		size = tdata[6];
+		orientation = (tdata[8] >> 5) - 4;
+		touch_major = tdata[4];
+		touch_minor = tdata[5];
+		/* Add to pressure to prevent libraries such as libinput
+		 * from ignoring low pressure touches
+		 */
+		pressure = tdata[7] + 30;
+		state = tdata[3] & 0xC0;
+		down = state == 0x80;
 	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
 		id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
 		x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
@@ -215,7 +243,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
 	/* If requested, emulate a scroll wheel by detecting small
 	 * vertical touch motions.
 	 */
-	if (emulate_scroll_wheel) {
+	if (emulate_scroll_wheel && (input->id.product !=
+			USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) {
 		unsigned long now = jiffies;
 		int step_x = msc->touches[id].scroll_x - x;
 		int step_y = msc->touches[id].scroll_y - y;
@@ -269,10 +298,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
 		input_report_abs(input, ABS_MT_POSITION_X, x);
 		input_report_abs(input, ABS_MT_POSITION_Y, y);
 
+		if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
+			input_report_abs(input, ABS_MT_PRESSURE, pressure);
+
 		if (report_undeciphered) {
 			if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
 				input_event(input, EV_MSC, MSC_RAW, tdata[7]);
-			else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+			else if (input->id.product !=
+					USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
 				input_event(input, EV_MSC, MSC_RAW, tdata[8]);
 		}
 	}
@@ -287,6 +320,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 
 	switch (data[0]) {
 	case TRACKPAD_REPORT_ID:
+	case TRACKPAD2_BT_REPORT_ID:
 		/* Expect four bytes of prefix, and N*9 bytes of touch data. */
 		if (size < 4 || ((size - 4) % 9) != 0)
 			return 0;
@@ -301,12 +335,22 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 			magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
 
 		clicks = data[1];
+		break;
+	case TRACKPAD2_USB_REPORT_ID:
+		/* Expect twelve bytes of prefix and N*9 bytes of touch data. */
+		if (size < 12 || ((size - 12) % 9) != 0)
+			return 0;
+		npoints = (size - 12) / 9;
+		if (npoints > 15) {
+			hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n",
+					size);
+			return 0;
+		}
+		msc->ntouches = 0;
+		for (ii = 0; ii < npoints; ii++)
+			magicmouse_emit_touch(msc, ii, data + ii * 9 + 12);
 
-		/* The following bits provide a device specific timestamp. They
-		 * are unused here.
-		 *
-		 * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
-		 */
+		clicks = data[1];
 		break;
 	case MOUSE_REPORT_ID:
 		/* Expect six bytes of prefix, and N*8 bytes of touch data. */
@@ -352,6 +396,9 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 		magicmouse_emit_buttons(msc, clicks & 3);
 		input_report_rel(input, REL_X, x);
 		input_report_rel(input, REL_Y, y);
+	} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		input_mt_sync_frame(input);
+		input_report_key(input, BTN_MOUSE, clicks & 1);
 	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
 		input_report_key(input, BTN_MOUSE, clicks & 1);
 		input_mt_report_pointer_emulation(input, true);
@@ -364,6 +411,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
 {
 	int error;
+	int mt_flags = 0;
 
 	__set_bit(EV_KEY, input->evbit);
 
@@ -380,6 +428,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 			__set_bit(REL_WHEEL, input->relbit);
 			__set_bit(REL_HWHEEL, input->relbit);
 		}
+	} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		/* setting the device name to ensure the same driver settings
+		 * get loaded, whether connected through bluetooth or USB
+		 */
+		input->name = "Apple Inc. Magic Trackpad 2";
+
+		__clear_bit(EV_MSC, input->evbit);
+		__clear_bit(BTN_0, input->keybit);
+		__clear_bit(BTN_RIGHT, input->keybit);
+		__clear_bit(BTN_MIDDLE, input->keybit);
+		__set_bit(BTN_MOUSE, input->keybit);
+		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+		__set_bit(BTN_TOOL_FINGER, input->keybit);
+
+		mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+				INPUT_MT_TRACK;
 	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
 		/* input->keybit is initialized with incorrect button info
 		 * for Magic Trackpad. There really is only one physical
@@ -402,14 +466,13 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 
 	__set_bit(EV_ABS, input->evbit);
 
-	error = input_mt_init_slots(input, 16, 0);
+	error = input_mt_init_slots(input, 16, mt_flags);
 	if (error)
 		return error;
 	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
 			     4, 0);
 	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
 			     4, 0);
-	input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
 
 	/* Note: Touch Y position from the device is inverted relative
 	 * to how pointer motion is reported (and relative to how USB
@@ -418,6 +481,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 	 * inverse of the reported Y.
 	 */
 	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+		input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
 		input_set_abs_params(input, ABS_MT_POSITION_X,
 				     MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
 		input_set_abs_params(input, ABS_MT_POSITION_Y,
@@ -427,7 +491,25 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 				  MOUSE_RES_X);
 		input_abs_set_res(input, ABS_MT_POSITION_Y,
 				  MOUSE_RES_Y);
+	} else if (input->id.product ==  USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		input_set_abs_params(input, ABS_MT_PRESSURE, 0, 283, 0, 0);
+		input_set_abs_params(input, ABS_PRESSURE, 0, 283, 0, 0);
+		input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0);
+		input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X,
+				     TRACKPAD2_MAX_X, 0, 0);
+		input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y,
+				     TRACKPAD2_MAX_Y, 0, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_X,
+				     TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y,
+				     TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0);
+
+		input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X);
+		input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y);
+		input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X);
+		input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y);
 	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+		input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
 		input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
 				     TRACKPAD_MAX_X, 4, 0);
 		input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
@@ -447,7 +529,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 
 	input_set_events_per_packet(input, 60);
 
-	if (report_undeciphered) {
+	if (report_undeciphered &&
+	    input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
 		__set_bit(EV_MSC, input->evbit);
 		__set_bit(MSC_RAW, input->mscbit);
 	}
@@ -465,7 +548,8 @@ static int magicmouse_input_mapping(struct hid_device *hdev,
 		msc->input = hi->input;
 
 	/* Magic Trackpad does not give relative data after switching to MT */
-	if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD &&
+	if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD ||
+	     hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
 	    field->flags & HID_MAIN_ITEM_RELATIVE)
 		return -1;
 
@@ -494,11 +578,20 @@ static int magicmouse_input_configured(struct hid_device *hdev,
 static int magicmouse_probe(struct hid_device *hdev,
 	const struct hid_device_id *id)
 {
-	const u8 feature[] = { 0xd7, 0x01 };
+	const u8 *feature;
+	const u8 feature_mt[] = { 0xD7, 0x01 };
+	const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
+	const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
 	u8 *buf;
 	struct magicmouse_sc *msc;
 	struct hid_report *report;
 	int ret;
+	int feature_size;
+
+	if (id->vendor == USB_VENDOR_ID_APPLE &&
+	    id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
+	    hdev->type != HID_TYPE_USBMOUSE)
+		return 0;
 
 	msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
 	if (msc == NULL) {
@@ -532,7 +625,14 @@ static int magicmouse_probe(struct hid_device *hdev,
 	if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
 		report = hid_register_report(hdev, HID_INPUT_REPORT,
 			MOUSE_REPORT_ID, 0);
-	else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+	else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		if (id->vendor == BT_VENDOR_ID_APPLE)
+			report = hid_register_report(hdev, HID_INPUT_REPORT,
+				TRACKPAD2_BT_REPORT_ID, 0);
+		else /* USB_VENDOR_ID_APPLE */
+			report = hid_register_report(hdev, HID_INPUT_REPORT,
+				TRACKPAD2_USB_REPORT_ID, 0);
+	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
 		report = hid_register_report(hdev, HID_INPUT_REPORT,
 			TRACKPAD_REPORT_ID, 0);
 		report = hid_register_report(hdev, HID_INPUT_REPORT,
@@ -546,7 +646,20 @@ static int magicmouse_probe(struct hid_device *hdev,
 	}
 	report->size = 6;
 
-	buf = kmemdup(feature, sizeof(feature), GFP_KERNEL);
+	if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+		if (id->vendor == BT_VENDOR_ID_APPLE) {
+			feature_size = sizeof(feature_mt_trackpad2_bt);
+			feature = feature_mt_trackpad2_bt;
+		} else { /* USB_VENDOR_ID_APPLE */
+			feature_size = sizeof(feature_mt_trackpad2_usb);
+			feature = feature_mt_trackpad2_usb;
+		}
+	} else {
+		feature_size = sizeof(feature_mt);
+		feature = feature_mt;
+	}
+
+	buf = kmemdup(feature, feature_size, GFP_KERNEL);
 	if (!buf) {
 		ret = -ENOMEM;
 		goto err_stop_hw;
@@ -560,10 +673,10 @@ static int magicmouse_probe(struct hid_device *hdev,
 	 * but there seems to be no other way of switching the mode.
 	 * Thus the super-ugly hacky success check below.
 	 */
-	ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature),
+	ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
 				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 	kfree(buf);
-	if (ret != -EIO && ret != sizeof(feature)) {
+	if (ret != -EIO && ret != feature_size) {
 		hid_err(hdev, "unable to request touch data (%d)\n", ret);
 		goto err_stop_hw;
 	}
@@ -579,6 +692,10 @@ static const struct hid_device_id magic_mice[] = {
 		USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
 		USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
+	{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
+		USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
+		USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, magic_mice);
-- 
2.19.0.444.g18242da7ef-goog

^ permalink raw reply related

* KMSAN: uninit-value in synaptics_detect
From: syzbot @ 2018-09-20 21:04 UTC (permalink / raw)
  To: aaron.ma, aduggan, benjamin.tissoires, dmitry.torokhov, kt.liao,
	linux-input, linux-kernel, peter.hutterer, rydberg,
	syzkaller-bugs, zhenjie.wang

Hello,

syzbot found the following crash on:

HEAD commit:    42a037ca8d9d kmsan: update README.md to reference LLVM r34..
git tree:       https://github.com/google/kmsan.git/master
console output: https://syzkaller.appspot.com/x/log.txt?x=1392c149400000
kernel config:  https://syzkaller.appspot.com/x/.config?x=3431f03869413153
dashboard link: https://syzkaller.appspot.com/bug?extid=13cb3b01d0784e4ffc3f
compiler:       clang version 8.0.0 (trunk 339414)
syz repro:      https://syzkaller.appspot.com/x/repro.syz?x=14616421400000
C reproducer:   https://syzkaller.appspot.com/x/repro.c?x=16a164d1400000

IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+13cb3b01d0784e4ffc3f@syzkaller.appspotmail.com

random: sshd: uninitialized urandom read (32 bytes read)
random: sshd: uninitialized urandom read (32 bytes read)
psmouse serio2: Failed to reset mouse on : -5
misc userio: Buffer overflowed, userio client isn't keeping up
==================================================================
BUG: KMSAN: uninit-value in synaptics_detect+0x1fa/0x2a0  
drivers/input/mouse/synaptics.c:112
CPU: 1 PID: 41 Comm: kworker/1:2 Not tainted 4.19.0-rc1+ #42
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS  
Google 01/01/2011
Workqueue: events_long serio_handle_event
Call Trace:
  __dump_stack lib/dump_stack.c:77 [inline]
  dump_stack+0x14b/0x190 lib/dump_stack.c:113
  kmsan_report+0x183/0x2b0 mm/kmsan/kmsan.c:956
  __msan_warning+0x70/0xc0 mm/kmsan/kmsan_instr.c:645
  synaptics_detect+0x1fa/0x2a0 drivers/input/mouse/synaptics.c:112
  psmouse_do_detect drivers/input/mouse/psmouse-base.c:1011 [inline]
  psmouse_extensions+0x10fd/0x3820 drivers/input/mouse/psmouse-base.c:1106
  psmouse_switch_protocol+0x184/0xd90 drivers/input/mouse/psmouse-base.c:1544
  psmouse_connect+0x1387/0x2290 drivers/input/mouse/psmouse-base.c:1634
  serio_connect_driver drivers/input/serio/serio.c:59 [inline]
  serio_driver_probe+0xe3/0x150 drivers/input/serio/serio.c:790
  really_probe+0x19ae/0x2040 drivers/base/dd.c:500
  driver_probe_device+0x1b4/0x4f0 drivers/base/dd.c:662
  __device_attach_driver+0x632/0x750 drivers/base/dd.c:758
  bus_for_each_drv+0x27e/0x390 drivers/base/bus.c:461
  __device_attach+0x381/0x5e0 drivers/base/dd.c:815
  device_initial_probe+0x4a/0x60 drivers/base/dd.c:862
  bus_probe_device+0x137/0x390 drivers/base/bus.c:521
  device_add+0x2687/0x2c70 drivers/base/core.c:1927
  serio_add_port drivers/input/serio/serio.c:554 [inline]
  serio_handle_event+0x1d90/0x2700 drivers/input/serio/serio.c:222
  process_one_work+0x1605/0x1f40 kernel/workqueue.c:2153
  worker_thread+0x11a2/0x2590 kernel/workqueue.c:2296
  kthread+0x465/0x4a0 kernel/kthread.c:247
  ret_from_fork+0x35/0x40 arch/x86/entry/entry_64.S:416

Local variable description: ----param@synaptics_detect
Variable was created at:
  synaptics_detect+0x50/0x2a0 drivers/input/mouse/synaptics.c:100
  psmouse_do_detect drivers/input/mouse/psmouse-base.c:1011 [inline]
  psmouse_extensions+0x10fd/0x3820 drivers/input/mouse/psmouse-base.c:1106
==================================================================


---
This bug is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this bug report. See:
https://goo.gl/tpsmEJ#bug-status-tracking for how to communicate with  
syzbot.
syzbot can test patches for this bug, for details see:
https://goo.gl/tpsmEJ#testing-patches

^ permalink raw reply

* Re: [RFC/PATCH 2/5] device property: introduce notion of subnodes for legacy boards
From: Heikki Krogerus @ 2018-09-20 13:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180917181603.125492-3-dmitry.torokhov@gmail.com>

Hi Dmitry,

On Mon, Sep 17, 2018 at 11:16:00AM -0700, Dmitry Torokhov wrote:
> +/**
> + * device_add_child_properties - Add a collection of properties to a device object.
> + * @dev: Device to add properties to.

In case you didn't notice my comment for this, you are missing @parent
here.

But why do you need both the parent and the dev?

> + * @properties: Collection of properties to add.
> + *
> + * Associate a collection of device properties represented by @properties as a
> + * child of given @parent firmware node.  The function takes a copy of
> + * @properties.
> + */
> +struct fwnode_handle *
> +device_add_child_properties(struct device *dev,
> +			    struct fwnode_handle *parent,
> +			    const struct property_entry *properties)
> +{
> +	struct property_set *p;
> +	struct property_set *parent_pset;
> +
> +	if (!properties)
> +		return ERR_PTR(-EINVAL);
> +
> +	parent_pset = to_pset_node(parent);

For this function, the parent will in practice have to be
dev_fwnode(dev), so I don't think you need @parent at all, no?

There is something wrong here..

> +	if (!parent_pset)
> +		return ERR_PTR(-EINVAL);
> +
> +	p = pset_create_set(properties);
> +	if (IS_ERR(p))
> +		return ERR_CAST(p);
> +
> +	p->dev = dev;

That looks wrong.

I'm guessing the assumption here is that the child nodes will never be
assigned to their own devices, but you can't do that. It will limit
the use of the child nodes to a very small number of cases, possibly
only to gpios.

I think that has to be fixed. It should not be a big deal. Just expect
the child nodes to be removed separately, and add ref counting to the
struct property_set handling.

> +	p->parent = parent_pset;
> +	list_add_tail(&p->child_node, &parent_pset->children);
> +
> +	return &p->fwnode;
> +}
> +EXPORT_SYMBOL_GPL(device_add_child_properties);

The child nodes will change the purpose of the build-in property
support. Originally the goal was just to support adding of build-in
device properties to real firmware nodes, but things have changed
quite a bit from that. These child nodes are purely tied to the
build-in device property support, so we should be talking about adding
pset type child nodes to pset type parent nodes in the API:
fwnode_pset_add_child_node(), or something like that.

I'm sorry to be a bit of pain in the butt with this. The build-in
property support is a hack, it always was. A useful hack, but hack
nevertheless. That means we need to be extra careful when expanding
it, like here.


Thanks,

-- 
heikki

^ permalink raw reply

* RE: [RESEND PATCH V6 3/3] Input: new da7280 haptic driver
From: Roy Im @ 2018-09-20 12:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Roy Im
  Cc: Rob Herring, Mark Rutland, Support Opensource,
	devicetree@vger.kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <20180920003510.GA228805@dtor-ws>

Hi Dmitry,

On Thursday, September 20, 2018 9:35 AM, Dmitry wrote:
> 
> Hi Roy,
> 
> On Wed, Sep 19, 2018 at 07:35:04PM +0900, Roy Im wrote:
> >
> > 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>
> >
> > ---
> > 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 | 1438 +++++++++++++++++++++++++++++++++++++++++++
> >  drivers/input/misc/da7280.h |  412 +++++++++++++
> >  4 files changed, 1864 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..041a9f4
> > --- /dev/null
> > +++ b/drivers/input/misc/da7280.c
> > @@ -0,0 +1,1438 @@
> > +// 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;
> > +
> > +	bool suspend_state;
> > +	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;
> > +	u8 bemf_sense_en;
> > +	u8 freq_track_en;
> > +	u8 acc_en;
> > +	u8 rapid_stop_en;
> > +	u8 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];
> > +	const struct attribute_group **attr_group;
> > +};
> > +
> > +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 ret;
> > +	unsigned int val;
> > +
> > +	/* It is recommended to update the patterns
> > +	 * during haptic is not working in order to avoid conflict
> > +	 */
> > +	ret = regmap_read(haptics->regmap, DA7280_IRQ_STATUS1, &val);
> > +	if (ret)
> > +		return ret;
> > +	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;
> > +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL2, &val);
> > +	if (ret)
> > +		return ret;
> > +	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 */
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_OPERATION_MODE_MASK,
> > +				 0);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	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 ret;
> > +
> > +	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;
> > +
> > +	ret = pwm_config(haptics->pwm_dev,
> > +			 pwm_duty, pargs.period);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to configure pwm : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = pwm_enable(haptics->pwm_dev);
> > +	if (ret) {
> > +		pwm_disable(haptics->pwm_dev);
> > +		dev_err(haptics->dev,
> > +			"failed to enable haptics pwm device : %d\n", ret);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void da7280_haptic_enable(struct da7280_haptic *haptics)
> > +{
> > +	int ret = 0;
> > +
> > +	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)
> > +		 */
> > +		ret = regmap_write(haptics->regmap,
> > +				   DA7280_TOP_CTL2,
> > +				   haptics->magnitude);
> > +		if (ret) {
> > +			dev_err(haptics->dev,
> > +				"i2c err for driving level set : %d\n",
> > +				ret);
> > +			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;
> > +	}
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_OPERATION_MODE_MASK,
> > +				 haptics->op_mode);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"i2c err for op_mode setting : %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	if (haptics->op_mode == DA7280_PWM_MODE ||
> > +	    haptics->op_mode == DA7280_RTWM_MODE) {
> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CTL1,
> > +					 DA7280_SEQ_START_MASK,
> > +					 DA7280_SEQ_START_MASK);
> > +		if (ret)
> > +			dev_err(haptics->dev,
> > +				"i2c err for sequence triggering : %d\n", ret);
> > +	}
> > +}
> > +
> > +static void da7280_haptic_disable(struct da7280_haptic *haptics)
> > +{
> > +	int ret;
> > +
> > +	/* Set to Inactive mode */
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_OPERATION_MODE_MASK, 0);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"i2c err for op_mode off : %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	switch (haptics->op_mode) {
> > +	case DA7280_RTWM_MODE:
> > +	case DA7280_ETWM_MODE:
> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CTL1,
> > +					 DA7280_SEQ_START_MASK, 0);
> > +		if (ret) {
> > +			dev_err(haptics->dev,
> > +				"i2c err for RTWM or ETWM mode off : %d\n",
> > +				ret);
> > +			return;
> > +		}
> > +		break;
> > +	case DA7280_DRO_MODE:
> > +		ret = regmap_write(haptics->regmap,
> > +				   DA7280_TOP_CTL2, 0);
> > +		if (ret) {
> > +			dev_err(haptics->dev,
> > +				"i2c err for DRO mode off : %d\n",
> > +				ret);
> > +			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;
> > +	}
> > +}
> > +
> > +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 ret;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_STANDBY_EN_MASK,
> > +				 DA7280_STANDBY_EN_MASK);
> > +	if (ret)
> > +		dev_err(haptics->dev,
> > +			"Failed to open haptic, i2c error : %d\n", ret);
> > +
> > +	return ret;
> > +}
> > +
> > +static void da7280_haptic_close(struct input_dev *dev)
> > +{
> > +	struct da7280_haptic *haptics = input_get_drvdata(dev);
> > +	int ret;
> > +
> > +	cancel_work_sync(&haptics->work);
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_OPERATION_MODE_MASK, 0);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	if (haptics->op_mode == DA7280_DRO_MODE) {
> > +		ret = regmap_write(haptics->regmap,
> > +				   DA7280_TOP_CTL2, 0);
> > +
> > +		if (ret)
> > +			goto error_i2c;
> > +	}
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_STANDBY_EN_MASK, 0);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	return;
> > +
> > +error_i2c:
> > +	dev_err(haptics->dev, "DA7280-haptic i2c error : %d\n", ret);
> > +}
> > +
> > +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_of_to_pdata(struct device *dev,
> > +			       struct da7280_haptic *haptics)
> > +{
> > +	struct device_node *np = dev->of_node;
> > +	char dt_gpi_str1[] = "dlg,gpi0-seq-id";
> > +	char dt_gpi_str2[] = "dlg,gpi0-mode";
> > +	char dt_gpi_str3[] = "dlg,gpi0-polarity";
> > +	unsigned int mem[DA7280_SNP_MEM_SIZE];
> > +	const char *of_str;
> > +	u32 of_val32;
> > +	int i;
> > +
> > +	if (!of_property_read_string(np, "dlg,actuator-type", &of_str))
> > +		haptics->dev_type =
> > +			da7280_haptic_of_mode_str(dev, of_str);
> > +	else /* if no property, then use the mode inside chip */
> > +		haptics->dev_type = DA7280_DEV_MAX;
> 
> Nothing says that this device can only be used in OF systems. Please
> switch to generic device properties:
> 
> 	if (!device_property_read_string(dev, "dlg,actuator-type", &of_str))
> 		...
> 

I will switch to them in V7

> > +
> > +	if (of_property_read_u32(np, "dlg,op-mode", &of_val32) >= 0)
> > +		if (of_val32 && of_val32 < DA7280_OPMODE_MAX)
> > +			haptics->op_mode = of_val32;
> > +		else
> > +			haptics->op_mode = DA7280_DRO_MODE;
> > +	else
> > +		haptics->op_mode = DA7280_DRO_MODE;
> > +
> > +	if (of_property_read_u32(np, "dlg,nom-microvolt", &of_val32) >= 0)
> > +		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
> > +			haptics->nommax =
> > +				da7280_haptic_of_volt_rating_set(of_val32);
> > +		else
> > +			haptics->nommax = DA7280_SKIP_INIT;
> > +	else /* if no property, then use the value inside chip */
> > +		haptics->nommax = DA7280_SKIP_INIT;
> > +
> > +	if (of_property_read_u32(np, "dlg,abs-max-microvolt", &of_val32) >= 0)
> > +		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
> > +			haptics->absmax =
> > +				da7280_haptic_of_volt_rating_set(of_val32);
> > +		else
> > +			haptics->absmax = DA7280_SKIP_INIT;
> > +	else
> > +		haptics->absmax = DA7280_SKIP_INIT;
> > +
> > +	if (of_property_read_u32(np, "dlg,imax-microamp", &of_val32) >= 0)
> > +		if (of_val32 < DA7280_IMAX_LIMIT)
> > +			haptics->imax = (of_val32 - 28600)
> > +					/ DA7280_IMAX_STEP + 1;
> > +		else
> > +			haptics->imax = DA7280_IMAX_DEFAULT;
> > +	else
> > +		haptics->imax = DA7280_IMAX_DEFAULT;
> > +
> > +	if (of_property_read_u32(np, "dlg,impd-micro-ohms", &of_val32) >= 0)
> > +		if (of_val32 <= DA7280_IMPD_MAX)
> > +			haptics->impd = of_val32;
> > +		else
> > +			haptics->impd = DA7280_IMPD_DEFAULT;
> > +	else
> > +		haptics->impd = DA7280_IMPD_DEFAULT;
> > +
> > +	if (of_property_read_u32(np, "dlg,resonant-freq-hz", &of_val32) >= 0) {
> > +		if (of_val32 < DA7280_MAX_RESONAT_FREQ_HZ &&
> > +		    of_val32 > DA7280_MIN_RESONAT_FREQ_HZ) {
> > +			haptics->resonant_freq_h =
> > +				((1000000000 / (of_val32 * 1333)) >> 7) & 0xFF;
> > +			haptics->resonant_freq_l =
> > +				(1000000000 / (of_val32 * 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 (of_property_read_u32(np, "dlg,ps-seq-id", &of_val32) >= 0)
> > +		if (of_val32 <= DA7280_SEQ_ID_MAX)
> > +			haptics->ps_seq_id = of_val32;
> > +		else
> > +			haptics->ps_seq_id = 0;
> > +	else /* if no property, set to zero as a default do nothing */
> > +		haptics->ps_seq_id = 0;
> > +
> > +	if (of_property_read_u32(np, "dlg,ps-seq-loop", &of_val32) >= 0)
> > +		if (of_val32 <= DA7280_SEQ_LOOP_MAX)
> > +			haptics->ps_seq_loop = of_val32;
> > +		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++) {
> > +		dt_gpi_str1[7] = '0' + i;
> > +		if (of_property_read_u32(np, dt_gpi_str1, &of_val32) >= 0)
> > +			if (of_val32 <= DA7280_SEQ_ID_MAX)
> > +				haptics->gpi_ctl[i].seq_id = of_val32;
> > +			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;
> > +
> > +		dt_gpi_str2[7] = '0' + i;
> > +		if (!of_property_read_string(np, dt_gpi_str2, &of_str))
> > +			haptics->gpi_ctl[i].mode =
> > +				da7280_haptic_of_gpi_mode_str(dev, of_str);
> > +		else
> > +			haptics->gpi_ctl[i].mode = 0;
> > +
> > +		dt_gpi_str3[7] = '0' + i;
> > +		if (!of_property_read_string(np, dt_gpi_str3, &of_str))
> > +			haptics->gpi_ctl[i].polarity =
> > +				da7280_haptic_of_gpi_pol_str(dev, of_str);
> > +		else
> > +			haptics->gpi_ctl[i].polarity = 0;
> > +	}
> > +
> > +	haptics->bemf_sense_en =
> > +		of_property_read_bool(np, "dlg,bemf-sens-enable");
> > +	haptics->freq_track_en =
> > +		of_property_read_bool(np, "dlg,freq-track-enable");
> > +	haptics->acc_en =
> > +		of_property_read_bool(np, "dlg,acc-enable");
> > +	haptics->rapid_stop_en =
> > +		of_property_read_bool(np, "dlg,rapid-stop-enable");
> > +	haptics->amp_pid_en =
> > +		of_property_read_bool(np, "dlg,amp-pid-enable");
> > +
> > +	if (of_property_read_u32_array(np, "dlg,mem-array",
> > +				       &mem[0], DA7280_SNP_MEM_SIZE) >= 0) {
> > +		haptics->mem_update = 1;
> > +		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 = 0;
> > +	}
> > +}
> > +
> > +static irqreturn_t da7280_irq_handler(int irq, void *data)
> > +{
> > +	struct da7280_haptic *haptics = data;
> > +	u8 events[IRQ_NUM];
> > +	int ret;
> > +
> > +	/* Check what events have happened */
> > +	ret = regmap_bulk_read(haptics->regmap,
> > +			       DA7280_IRQ_EVENT1,
> > +			       events, IRQ_NUM);
> > +	if (ret)
> > +		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.
> > +		 */
> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CTL1,
> > +					 DA7280_OPERATION_MODE_MASK, 0);
> > +		if (ret)
> > +			goto error_i2c;
> > +	}
> > +
> > +	/* Clear events */
> > +	ret = regmap_write(haptics->regmap,
> > +			   DA7280_IRQ_EVENT1, events[0]);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	return IRQ_HANDLED;
> > +
> > +error_i2c:
> > +	dev_err(haptics->dev, "da7280 i2c error : %d\n", ret);
> > +	return IRQ_NONE;
> > +}
> > +
> > +static int da7280_init(struct da7280_haptic *haptics)
> > +{
> > +	int ret, 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) {
> > +		ret = regmap_read(haptics->regmap, DA7280_TOP_CFG1, &val);
> > +		if (ret)
> > +			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) {
> > +			ret = regmap_write(haptics->regmap,
> > +					   DA7280_FRQ_LRA_PER_H,
> > +					   haptics->resonant_freq_h);
> > +			if (ret)
> > +				goto error_i2c;
> > +			ret = regmap_write(haptics->regmap,
> > +					   DA7280_FRQ_LRA_PER_L,
> > +					   haptics->resonant_freq_l);
> > +			if (ret)
> > +				goto error_i2c;
> > +		}
> > +	} else if (haptics->dev_type == DA7280_ERM_COIN) {
> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_INT_CFG1,
> > +					 DA7280_BEMF_FAULT_LIM_MASK, 0);
> > +		if (ret)
> > +			goto error_i2c;
> > +
> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CFG4,
> > +					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
> > +					 DA7280_V2I_FACTOR_FREEZE_MASK,
> > +					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
> > +					 DA7280_V2I_FACTOR_FREEZE_MASK);
> > +		if (ret)
> > +			goto error_i2c;
> > +
> > +		haptics->acc_en = 0;
> > +		haptics->rapid_stop_en = 0;
> > +		haptics->amp_pid_en = 0;
> > +	}
> > +
> > +	/* Should be set to 0 only
> > +	 * in custom waveform and wideband operation
> > +	 */
> > +	if (haptics->op_mode >= DA7280_RTWM_MODE)
> > +		haptics->bemf_sense_en = 0;
> > +
> > +	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;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CFG1, mask, val);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	if (haptics->nommax != DA7280_SKIP_INIT) {
> > +		ret = regmap_write(haptics->regmap,
> > +				   DA7280_ACTUATOR1,
> > +				   haptics->nommax);
> > +		if (ret)
> > +			goto error_i2c;
> > +	}
> > +
> > +	if (haptics->absmax != DA7280_SKIP_INIT) {
> > +		ret = regmap_write(haptics->regmap, DA7280_ACTUATOR2,
> > +				   haptics->absmax);
> > +		if (ret)
> > +			goto error_i2c;
> > +	}
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_ACTUATOR3,
> > +				 DA7280_IMAX_MASK,
> > +				 haptics->imax);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	v2i_factor =
> > +		haptics->impd * (haptics->imax + 4) / 1610400;
> > +	ret = regmap_write(haptics->regmap,
> > +			   DA7280_CALIB_V2I_L,
> > +			   v2i_factor & 0xff);
> > +	if (ret)
> > +		goto error_i2c;
> > +	ret = regmap_write(haptics->regmap,
> > +			   DA7280_CALIB_V2I_H,
> > +			   v2i_factor >> 8);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_TOP_CTL1,
> > +				 DA7280_STANDBY_EN_MASK,
> > +				 DA7280_STANDBY_EN_MASK);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	if (haptics->mem_update) {
> > +		ret = da7280_haptic_mem_update(haptics);
> > +		if (ret)
> > +			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;
> > +	ret = regmap_write(haptics->regmap,
> > +			   DA7280_SEQ_CTL2, val);
> > +	if (ret)
> > +		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;
> > +		ret = regmap_write(haptics->regmap,
> > +				   DA7280_GPI_0_CTL + i, val);
> > +		if (ret)
> > +			goto error_i2c;
> > +	}
> > +
> > +	/* Clear Interrupts */
> > +	ret = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, 0xff);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_IRQ_MASK1,
> > +				 DA7280_SEQ_FAULT_M_MASK
> > +				 | DA7280_SEQ_DONE_M_MASK, 0);
> > +	if (ret)
> > +		goto error_i2c;
> > +
> > +	haptics->suspend_state = 0;
> 
> 	haptics->suspend_state = false;
> 

I will fix this in v7

> > +	return 0;
> > +
> > +error_i2c:
> > +	dev_err(haptics->dev, "haptic init - I2C error : %d\n", ret);
> > +	return ret;
> > +}
> > +
> > +/* 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 ret;
> > +
> > +	if (kstrtol(&buf[0], 0, &val) < 0)
> > +		goto err;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_SEQ_CTL2,
> > +				 DA7280_PS_SEQ_ID_MASK,
> > +				 (val & 0xf) >> DA7280_PS_SEQ_ID_SHIFT);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to update register : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	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 ret;
> > +	unsigned int val;
> > +
> > +	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to read register : %d\n", ret);
> > +		return ret;
> > +	}
> > +	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 ret;
> > +
> > +	if (kstrtol(&buf[0], 0, &val) < 0)
> > +		goto err;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_SEQ_CTL2,
> > +				 DA7280_PS_SEQ_LOOP_MASK,
> > +				 (val & 0xF) << DA7280_PS_SEQ_LOOP_SHIFT);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to update register : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	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 ret;
> > +	unsigned int val;
> > +
> > +	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to read register : %d\n", ret);
> > +		return ret;
> > +	}
> > +	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 Y > gpi_seq_id
> > + * ex) echo 2 15 > /sys/class/..../gpi_seq_id
> > + * 0 <= X < 3, 0<= Y <= 15.
> > + */
> 
> Sysfs attributes should ideally be one value per file. Sounds like you
> want 4 attributes here for individual GPIs?
> 

I will add 4 attributes instead of this in v7.

> > +static ssize_t gpi_seq_id_store(struct device *dev,
> > +				struct device_attribute *attr,
> > +				const char *buf,
> > +				size_t count)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	u8 gpi_num = 0xff;
> > +	long seq_id = 0xff;
> > +	int ret;
> > +
> > +	if (count < 4)
> > +		goto err;
> > +
> > +	if (buf[0] >= '0')
> > +		gpi_num = buf[0] - '0';
> > +	else
> > +		goto err;
> > +
> > +	if (buf[1] != ' ')
> > +		goto err;
> > +
> > +	if (kstrtol(&buf[2], 0, &seq_id) < 0)
> > +		goto err;
> > +
> > +	if (gpi_num > 2 || seq_id > 0xf)
> > +		goto err;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_GPI_0_CTL + gpi_num,
> > +				 DA7280_GPI0_SEQUENCE_ID_MASK,
> > +				 seq_id << DA7280_GPI0_SEQUENCE_ID_SHIFT);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to update register : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	haptics->gpi_ctl[gpi_num].seq_id = seq_id;
> > +
> > +	return count;
> > +
> > +err:
> > +	dev_err(dev, "Invalid format or values!\n");
> > +	return count;
> > +}
> > +
> > +static ssize_t gpi_seq_id_show(struct device *dev,
> > +			       struct device_attribute *attr,
> > +			       char *buf)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	int ret;
> > +	unsigned int val, i;
> > +
> > +	for (i = 0; i < 3; i++) {
> > +		ret = regmap_read(haptics->regmap,
> > +				  DA7280_GPI_0_CTL + i, &val);
> > +		if (ret) {
> > +			dev_err(haptics->dev,
> > +				"failed to read register : %d\n", ret);
> > +			return ret;
> > +		}
> > +		haptics->gpi_ctl[i].seq_id =
> > +			(val & DA7280_GPI0_SEQUENCE_ID_MASK)
> > +				>> DA7280_GPI0_SEQUENCE_ID_SHIFT;
> > +		val = 0;
> > +	}
> > +
> > +	return sprintf(buf,
> > +		"Seq ID\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> > +		haptics->gpi_ctl[0].seq_id,
> > +		haptics->gpi_ctl[1].seq_id,
> > +		haptics->gpi_ctl[2].seq_id);
> > +}
> > +
> > +/* Valid format for GPIx_MODE
> > + * echo X Y > gpi_mode
> > + * ex) echo 2 1 > /sys/class/..../gpi_mode
> > + * 0 <= X < 3, 0<= Y <= 1.
> > + */
> > +static ssize_t gpi_mode_store(struct device *dev,
> > +			      struct device_attribute *attr,
> > +			      const char *buf, size_t count)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	u8 gpi_num = 0xff, gpi_mode = 0xff;
> > +	int ret;
> > +
> > +	if (count < 3)
> > +		goto err;
> > +
> > +	if (buf[0] >= '0')
> > +		gpi_num = buf[0] - '0';
> > +	if (buf[2] >= '0')
> > +		gpi_mode = buf[2] - '0';
> > +
> > +	if (gpi_num > 2 || gpi_mode > 1)
> > +		goto err;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_GPI_0_CTL + gpi_num,
> > +				 DA7280_GPI0_MODE_MASK,
> > +				 gpi_mode << DA7280_GPI0_MODE_SHIFT);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to update register : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	haptics->gpi_ctl[gpi_num].mode = gpi_mode;
> > +
> > +	return count;
> > +
> > +err:
> > +	dev_err(dev, "Invalid format!\n");
> > +	return count;
> > +}
> > +
> > +static ssize_t gpi_mode_show(struct device *dev,
> > +			     struct device_attribute *attr,
> > +			     char *buf)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	int ret;
> > +	unsigned int val, i;
> > +
> > +	for (i = 0; i < 3; i++) {
> > +		ret = regmap_read(haptics->regmap,
> > +				  DA7280_GPI_0_CTL + i, &val);
> > +		if (ret) {
> > +			dev_err(haptics->dev,
> > +				"failed to read register : %d\n", ret);
> > +			return ret;
> > +		}
> > +		haptics->gpi_ctl[i].mode =
> > +			(val & DA7280_GPI0_MODE_MASK)
> > +				>> DA7280_GPI0_MODE_SHIFT;
> > +		val = 0;
> > +	}
> > +
> > +	return sprintf(buf, "Mode\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> > +		haptics->gpi_ctl[0].mode,
> > +		haptics->gpi_ctl[1].mode,
> > +		haptics->gpi_ctl[2].mode);
> > +}
> > +
> > +/* Valid format for GPIx_MODE
> > + *  echo X Y > gpi_pol
> > + *  ex) echo 2 1 > /sys/class/..../gpi_pol
> > + *  0 <= X < 3, 0<= Y <= 2.
> > + */
> > +static ssize_t gpi_pol_store(struct device *dev,
> > +			     struct device_attribute *attr,
> > +			     const char *buf,
> > +			     size_t count)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	u8 gpi_pol = 0xff, gpi_num = 0xff;
> > +	int ret;
> > +
> > +	if (count < 3)
> > +		goto err;
> > +
> > +	if (buf[0] >= '0')
> > +		gpi_num = buf[0] - '0';
> > +	if (buf[2] >= '0')
> > +		gpi_pol = buf[2] - '0';
> > +
> > +	if (gpi_num > 2 || gpi_pol > 2)
> > +		goto err;
> > +
> > +	ret = regmap_update_bits(haptics->regmap,
> > +				 DA7280_GPI_0_CTL + gpi_num,
> > +				 DA7280_GPI0_POLARITY_MASK,
> > +				 gpi_pol << DA7280_GPI0_POLARITY_SHIFT);
> > +	if (ret) {
> > +		dev_err(haptics->dev,
> > +			"failed to update register : %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	haptics->gpi_ctl[gpi_num].polarity = gpi_pol;
> > +
> > +	return count;
> > +
> > +err:
> > +	dev_err(dev, "Invalid format or input values!\n");
> > +	return count;
> > +}
> > +
> > +static ssize_t gpi_pol_show(struct device *dev,
> > +			    struct device_attribute *attr,
> > +			    char *buf)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	int ret = 0;
> > +	unsigned int val, i;
> > +
> > +	for (i = 0; i < 3; i++) {
> > +		ret = regmap_read(haptics->regmap,
> > +				  DA7280_GPI_0_CTL + i, &val);
> > +		if (ret)
> > +			return ret;
> > +		haptics->gpi_ctl[i].polarity =
> > +			(val & DA7280_GPI0_POLARITY_MASK)
> > +			>> DA7280_GPI0_POLARITY_SHIFT;
> > +		val = 0;
> > +	}
> > +
> > +	return sprintf(buf, "Polarity\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> > +		haptics->gpi_ctl[0].polarity,
> > +		haptics->gpi_ctl[1].polarity,
> > +		haptics->gpi_ctl[2].polarity);
> > +}
> > +
> > +#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 ret;
> > +
> > +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	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);
> > +
> > +	ret = da7280_haptic_mem_update(haptics);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return count;
> > +}
> > +
> > +static DEVICE_ATTR_RW(ps_seq_id);
> > +static DEVICE_ATTR_RW(ps_seq_loop);
> > +static DEVICE_ATTR_RW(gpi_seq_id);
> > +static DEVICE_ATTR_RW(gpi_mode);
> > +static DEVICE_ATTR_RW(gpi_pol);
> > +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_id.attr,
> > +	&dev_attr_gpi_mode.attr,
> > +	&dev_attr_gpi_pol.attr,
> > +	&dev_attr_patterns.attr,
> > +	NULL,
> > +};
> > +
> > +static const struct attribute_group da7280_attr_group = {
> > +	.attrs = da7280_sysfs_attr,
> > +};
> > +
> > +static const struct attribute_group *da7280_attr_groups[] = {
> > +	&da7280_attr_group,
> > +	NULL,
> > +};
> > +
> > +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 ret;
> 
> I'd prefer if we called this "error"/

I will change it to "error" in v7

> 
> > +
> > +	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;
> > +	}
> > +
> > +	/* Handle DT data if provided */
> > +	if (client->dev.of_node)
> > +		da7280_of_to_pdata(&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)) {
> > +		ret = PTR_ERR(haptics->regmap);
> > +		dev_err(dev, "Failed to allocate register map : %d\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, client->irq, NULL,
> > +					da7280_irq_handler,
> > +					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> 
> Do not hard-code trigger, let platform set it up as needed. Use
> IRQF_ONESHOT only.

I will fix this in v7

> 
> > +					"da7280-haptics", haptics);
> > +	if (ret != 0) {
> > +		dev_err(dev,
> > +			"Failed to request IRQ : %d\n", client->irq);
> > +		return ret;
> > +	}
> > +
> > +	ret = da7280_init(haptics);
> > +	if (ret) {
> > +		dev_err(dev, "failed to initialize device\n");
> > +		return ret;
> > +	}
> > +
> > +	/* 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);
> > +
> > +	ret = input_ff_create_memless(haptics->input_dev, NULL,
> > +				      da7280_haptic_play);
> 
> The device seems to be able to load a custom waveform into memory, why
> do you use input_ff_create_memless() instead if setting it up as
> FF_CUSTOM?

The device has many modes to start/enable haptic driver. Two modes are triggered/enabled by only magnitude value without any memory like direct mode and PWM mode. So I have used the input_ff_create_memless() for that modes and for the waveform memory modes I added the way to load waveform data by dt-binding property and sysfs file then it can be used easily and then they are triggered by magnitude value as well and external GPI event, so I think it can be covered accordingly. 

> 
> > +	if (ret) {
> > +		dev_err(dev, "failed to create force-feedback\n");
> > +		return ret;
> > +	}
> > +
> > +#ifdef CONFIG_SYSFS
> > +	haptics->input_dev->dev.groups = da7280_attr_groups;
> 
> These properties control behavior of hardware device, not input device
> abstraction, so they should be attached to the platform device.
> 
> Please use devm_device_add_group() or devm_device_add_groups().
> 

I will fix this in v7

> > +#endif
> > +
> > +	ret = input_register_device(haptics->input_dev);
> > +	if (ret)
> > +		dev_err(dev, "failed to register input device\n");
> > +
> > +	return ret;
> > +}
> > +
> > +static int __maybe_unused da7280_suspend(struct device *dev)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	int ret = 0;
> > +
> > +	mutex_lock(&haptics->input_dev->mutex);
> 
> 
> 
> > +	if (haptics->suspend_state == 0) {
> 
> 	if (!haptics->suspend_state) ...
> 
> But how exactly can we get here if we already in suspend state?
> 
> Also, maybe we shoudl check if device is in fact opened by anyone?
> 
> Also, I think you should cancel outstanding work, if any.
> 

I will fix this in v7

> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CTL1,
> > +					 DA7280_STANDBY_EN_MASK, 0);
> > +		if (ret)
> > +			dev_err(haptics->dev,
> > +				"I2C error : %d\n", ret);
> > +		else
> > +			haptics->suspend_state = 1;
> 
> 						= true;
> 

I will fix this in v7

> > +	}
> > +	mutex_unlock(&haptics->input_dev->mutex);
> > +	return ret;
> > +}
> > +
> > +static int __maybe_unused da7280_resume(struct device *dev)
> > +{
> > +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> > +	int ret = 0;
> > +
> > +	mutex_lock(&haptics->input_dev->mutex);
> > +	if (haptics->suspend_state) {
> 
> How can we get here if we are not in suspend state.
> 

I will fix this in v7

> > +		ret = regmap_update_bits(haptics->regmap,
> > +					 DA7280_TOP_CTL1,
> > +					 DA7280_STANDBY_EN_MASK,
> > +					 DA7280_STANDBY_EN_MASK);
> > +		if (ret)
> > +			dev_err(haptics->dev,
> > +				"i2c error : %d\n", ret);
> > +		else
> > +			haptics->suspend_state = 0;
> 
> 			= false;
> 

I will fix this in v7

> > +	}
> > +	mutex_unlock(&haptics->input_dev->mutex);
> > +	return ret;
> > +}
> > +
> > +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)
> 
> I do not think these definitions are of interest to anyone outside the
> driver itself, why don't you keep them in the .c file?
> 

Although these definitions may not be of interest to users, those register definitions in a separate header are our preferred way and the header is local to the same directory as the driver source, so it's not going to be picked up from standard include paths. They are created automatically so it would help the maintainability if they are kept in their own file and also they are quite large data to keep them in the .c file. So, I would like to keep using the header file if you don't mind much.

> > +
> > +#endif
> > --
> > end-of-patch for RESEND PATCH V6
> >
> 
> Thanks.
> 
> --
> Dmitry

^ permalink raw reply

* Re: [RFC/PATCH 2/5] device property: introduce notion of subnodes for legacy boards
From: Heikki Krogerus @ 2018-09-20 10:16 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180919171326.GB116719@dtor-ws>

On Wed, Sep 19, 2018 at 10:13:26AM -0700, Dmitry Torokhov wrote:
> > > diff --git a/drivers/base/pset_property.c b/drivers/base/pset_property.c
> > > index 08ecc13080ae..63f2377aefe8 100644
> > > --- a/drivers/base/pset_property.c
> > > +++ b/drivers/base/pset_property.c
> > > @@ -18,6 +18,11 @@ struct property_set {
> > >  	struct device *dev;
> > >  	struct fwnode_handle fwnode;
> > >  	const struct property_entry *properties;
> > > +
> > > +	struct property_set *parent;
> > > +	/* Entry in parent->children list */
> > > +	struct list_head child_node;
> > > +	struct list_head children;
> > 
> > Add
> > 
> >         const char *name;
> > 
> > and you can implement also pset_get_named_child_node().
> 
> Or
> 	char name[];
> 
> to avoid separate allocation.

Let's not do that, especially if you are planning on exporting this
structure. If the name is coming from .rodata, there is no need to
allocate anything for the name. Check kstrdup_const().

> Alternatively, we can add it later when we need it, and add
> device_add_named_child_properties().
> 
> I'll leave it up to Rafael to decide.

Fair enough.


Thanks,

-- 
heikki

^ permalink raw reply

* [PATCH] Input: pm8941-pwrkey - Add pms405 pwrkey support
From: Bjorn Andersson @ 2018-09-20  1:49 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Mark Rutland, Vinod Koul,
	Bjorn Andersson
  Cc: linux-input, devicetree, linux-kernel

From: Vinod Koul <vkoul@kernel.org>

Update the binding and driver for pms405 pwrkey.

Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt | 1 +
 drivers/input/misc/pm8941-pwrkey.c                             | 1 +
 2 files changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt
index 34ab5763f494..736fba3bad54 100644
--- a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt
+++ b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt
@@ -8,6 +8,7 @@ PROPERTIES
 	Definition: must be one of:
 		    "qcom,pm8941-pwrkey"
 		    "qcom,pm8941-resin"
+		    "qcom,pms405-pwrkey"
 
 - reg:
 	Usage: required
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
index 48153e0ca19a..fccf63263c1c 100644
--- a/drivers/input/misc/pm8941-pwrkey.c
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -317,6 +317,7 @@ static const struct pm8941_data resin_data = {
 static const struct of_device_id pm8941_pwr_key_id_table[] = {
 	{ .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
 	{ .compatible = "qcom,pm8941-resin", .data = &resin_data },
+	{ .compatible = "qcom,pms405-pwrkey", .data = &pwrkey_data },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
-- 
2.18.0

^ permalink raw reply related

* Re: [RESEND PATCH V6 3/3] Input: new da7280 haptic driver
From: Dmitry Torokhov @ 2018-09-20  0:35 UTC (permalink / raw)
  To: Roy Im
  Cc: Rob Herring, Mark Rutland, Support Opensource, devicetree,
	linux-input, linux-kernel
In-Reply-To: <16c97389790bb9d37d247d18d0b51fdccc64e56a.1537353304.git.Roy.Im@diasemi.com>

Hi Roy,

On Wed, Sep 19, 2018 at 07:35:04PM +0900, Roy Im wrote:
> 
> 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>
> 
> ---
> 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 | 1438 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/input/misc/da7280.h |  412 +++++++++++++
>  4 files changed, 1864 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..041a9f4
> --- /dev/null
> +++ b/drivers/input/misc/da7280.c
> @@ -0,0 +1,1438 @@
> +// 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;
> +
> +	bool suspend_state;
> +	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;
> +	u8 bemf_sense_en;
> +	u8 freq_track_en;
> +	u8 acc_en;
> +	u8 rapid_stop_en;
> +	u8 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];
> +	const struct attribute_group **attr_group;
> +};
> +
> +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 ret;
> +	unsigned int val;
> +
> +	/* It is recommended to update the patterns
> +	 * during haptic is not working in order to avoid conflict
> +	 */
> +	ret = regmap_read(haptics->regmap, DA7280_IRQ_STATUS1, &val);
> +	if (ret)
> +		return ret;
> +	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;
> +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL2, &val);
> +	if (ret)
> +		return ret;
> +	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 */
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_OPERATION_MODE_MASK,
> +				 0);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
> +	if (ret)
> +		return ret;
> +
> +	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 ret;
> +
> +	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;
> +
> +	ret = pwm_config(haptics->pwm_dev,
> +			 pwm_duty, pargs.period);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to configure pwm : %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = pwm_enable(haptics->pwm_dev);
> +	if (ret) {
> +		pwm_disable(haptics->pwm_dev);
> +		dev_err(haptics->dev,
> +			"failed to enable haptics pwm device : %d\n", ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static void da7280_haptic_enable(struct da7280_haptic *haptics)
> +{
> +	int ret = 0;
> +
> +	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)
> +		 */
> +		ret = regmap_write(haptics->regmap,
> +				   DA7280_TOP_CTL2,
> +				   haptics->magnitude);
> +		if (ret) {
> +			dev_err(haptics->dev,
> +				"i2c err for driving level set : %d\n",
> +				ret);
> +			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;
> +	}
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_OPERATION_MODE_MASK,
> +				 haptics->op_mode);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"i2c err for op_mode setting : %d\n", ret);
> +		return;
> +	}
> +
> +	if (haptics->op_mode == DA7280_PWM_MODE ||
> +	    haptics->op_mode == DA7280_RTWM_MODE) {
> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CTL1,
> +					 DA7280_SEQ_START_MASK,
> +					 DA7280_SEQ_START_MASK);
> +		if (ret)
> +			dev_err(haptics->dev,
> +				"i2c err for sequence triggering : %d\n", ret);
> +	}
> +}
> +
> +static void da7280_haptic_disable(struct da7280_haptic *haptics)
> +{
> +	int ret;
> +
> +	/* Set to Inactive mode */
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_OPERATION_MODE_MASK, 0);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"i2c err for op_mode off : %d\n", ret);
> +		return;
> +	}
> +
> +	switch (haptics->op_mode) {
> +	case DA7280_RTWM_MODE:
> +	case DA7280_ETWM_MODE:
> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CTL1,
> +					 DA7280_SEQ_START_MASK, 0);
> +		if (ret) {
> +			dev_err(haptics->dev,
> +				"i2c err for RTWM or ETWM mode off : %d\n",
> +				ret);
> +			return;
> +		}
> +		break;
> +	case DA7280_DRO_MODE:
> +		ret = regmap_write(haptics->regmap,
> +				   DA7280_TOP_CTL2, 0);
> +		if (ret) {
> +			dev_err(haptics->dev,
> +				"i2c err for DRO mode off : %d\n",
> +				ret);
> +			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;
> +	}
> +}
> +
> +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 ret;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_STANDBY_EN_MASK,
> +				 DA7280_STANDBY_EN_MASK);
> +	if (ret)
> +		dev_err(haptics->dev,
> +			"Failed to open haptic, i2c error : %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static void da7280_haptic_close(struct input_dev *dev)
> +{
> +	struct da7280_haptic *haptics = input_get_drvdata(dev);
> +	int ret;
> +
> +	cancel_work_sync(&haptics->work);
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_OPERATION_MODE_MASK, 0);
> +	if (ret)
> +		goto error_i2c;
> +
> +	if (haptics->op_mode == DA7280_DRO_MODE) {
> +		ret = regmap_write(haptics->regmap,
> +				   DA7280_TOP_CTL2, 0);
> +
> +		if (ret)
> +			goto error_i2c;
> +	}
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_STANDBY_EN_MASK, 0);
> +	if (ret)
> +		goto error_i2c;
> +
> +	return;
> +
> +error_i2c:
> +	dev_err(haptics->dev, "DA7280-haptic i2c error : %d\n", ret);
> +}
> +
> +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_of_to_pdata(struct device *dev,
> +			       struct da7280_haptic *haptics)
> +{
> +	struct device_node *np = dev->of_node;
> +	char dt_gpi_str1[] = "dlg,gpi0-seq-id";
> +	char dt_gpi_str2[] = "dlg,gpi0-mode";
> +	char dt_gpi_str3[] = "dlg,gpi0-polarity";
> +	unsigned int mem[DA7280_SNP_MEM_SIZE];
> +	const char *of_str;
> +	u32 of_val32;
> +	int i;
> +
> +	if (!of_property_read_string(np, "dlg,actuator-type", &of_str))
> +		haptics->dev_type =
> +			da7280_haptic_of_mode_str(dev, of_str);
> +	else /* if no property, then use the mode inside chip */
> +		haptics->dev_type = DA7280_DEV_MAX;

Nothing says that this device can only be used in OF systems. Please
switch to generic device properties:

	if (!device_property_read_string(dev, "dlg,actuator-type", &of_str))
		...

> +
> +	if (of_property_read_u32(np, "dlg,op-mode", &of_val32) >= 0)
> +		if (of_val32 && of_val32 < DA7280_OPMODE_MAX)
> +			haptics->op_mode = of_val32;
> +		else
> +			haptics->op_mode = DA7280_DRO_MODE;
> +	else
> +		haptics->op_mode = DA7280_DRO_MODE;
> +
> +	if (of_property_read_u32(np, "dlg,nom-microvolt", &of_val32) >= 0)
> +		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
> +			haptics->nommax =
> +				da7280_haptic_of_volt_rating_set(of_val32);
> +		else
> +			haptics->nommax = DA7280_SKIP_INIT;
> +	else /* if no property, then use the value inside chip */
> +		haptics->nommax = DA7280_SKIP_INIT;
> +
> +	if (of_property_read_u32(np, "dlg,abs-max-microvolt", &of_val32) >= 0)
> +		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
> +			haptics->absmax =
> +				da7280_haptic_of_volt_rating_set(of_val32);
> +		else
> +			haptics->absmax = DA7280_SKIP_INIT;
> +	else
> +		haptics->absmax = DA7280_SKIP_INIT;
> +
> +	if (of_property_read_u32(np, "dlg,imax-microamp", &of_val32) >= 0)
> +		if (of_val32 < DA7280_IMAX_LIMIT)
> +			haptics->imax = (of_val32 - 28600)
> +					/ DA7280_IMAX_STEP + 1;
> +		else
> +			haptics->imax = DA7280_IMAX_DEFAULT;
> +	else
> +		haptics->imax = DA7280_IMAX_DEFAULT;
> +
> +	if (of_property_read_u32(np, "dlg,impd-micro-ohms", &of_val32) >= 0)
> +		if (of_val32 <= DA7280_IMPD_MAX)
> +			haptics->impd = of_val32;
> +		else
> +			haptics->impd = DA7280_IMPD_DEFAULT;
> +	else
> +		haptics->impd = DA7280_IMPD_DEFAULT;
> +
> +	if (of_property_read_u32(np, "dlg,resonant-freq-hz", &of_val32) >= 0) {
> +		if (of_val32 < DA7280_MAX_RESONAT_FREQ_HZ &&
> +		    of_val32 > DA7280_MIN_RESONAT_FREQ_HZ) {
> +			haptics->resonant_freq_h =
> +				((1000000000 / (of_val32 * 1333)) >> 7) & 0xFF;
> +			haptics->resonant_freq_l =
> +				(1000000000 / (of_val32 * 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 (of_property_read_u32(np, "dlg,ps-seq-id", &of_val32) >= 0)
> +		if (of_val32 <= DA7280_SEQ_ID_MAX)
> +			haptics->ps_seq_id = of_val32;
> +		else
> +			haptics->ps_seq_id = 0;
> +	else /* if no property, set to zero as a default do nothing */
> +		haptics->ps_seq_id = 0;
> +
> +	if (of_property_read_u32(np, "dlg,ps-seq-loop", &of_val32) >= 0)
> +		if (of_val32 <= DA7280_SEQ_LOOP_MAX)
> +			haptics->ps_seq_loop = of_val32;
> +		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++) {
> +		dt_gpi_str1[7] = '0' + i;
> +		if (of_property_read_u32(np, dt_gpi_str1, &of_val32) >= 0)
> +			if (of_val32 <= DA7280_SEQ_ID_MAX)
> +				haptics->gpi_ctl[i].seq_id = of_val32;
> +			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;
> +
> +		dt_gpi_str2[7] = '0' + i;
> +		if (!of_property_read_string(np, dt_gpi_str2, &of_str))
> +			haptics->gpi_ctl[i].mode =
> +				da7280_haptic_of_gpi_mode_str(dev, of_str);
> +		else
> +			haptics->gpi_ctl[i].mode = 0;
> +
> +		dt_gpi_str3[7] = '0' + i;
> +		if (!of_property_read_string(np, dt_gpi_str3, &of_str))
> +			haptics->gpi_ctl[i].polarity =
> +				da7280_haptic_of_gpi_pol_str(dev, of_str);
> +		else
> +			haptics->gpi_ctl[i].polarity = 0;
> +	}
> +
> +	haptics->bemf_sense_en =
> +		of_property_read_bool(np, "dlg,bemf-sens-enable");
> +	haptics->freq_track_en =
> +		of_property_read_bool(np, "dlg,freq-track-enable");
> +	haptics->acc_en =
> +		of_property_read_bool(np, "dlg,acc-enable");
> +	haptics->rapid_stop_en =
> +		of_property_read_bool(np, "dlg,rapid-stop-enable");
> +	haptics->amp_pid_en =
> +		of_property_read_bool(np, "dlg,amp-pid-enable");
> +
> +	if (of_property_read_u32_array(np, "dlg,mem-array",
> +				       &mem[0], DA7280_SNP_MEM_SIZE) >= 0) {
> +		haptics->mem_update = 1;
> +		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 = 0;
> +	}
> +}
> +
> +static irqreturn_t da7280_irq_handler(int irq, void *data)
> +{
> +	struct da7280_haptic *haptics = data;
> +	u8 events[IRQ_NUM];
> +	int ret;
> +
> +	/* Check what events have happened */
> +	ret = regmap_bulk_read(haptics->regmap,
> +			       DA7280_IRQ_EVENT1,
> +			       events, IRQ_NUM);
> +	if (ret)
> +		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.
> +		 */
> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CTL1,
> +					 DA7280_OPERATION_MODE_MASK, 0);
> +		if (ret)
> +			goto error_i2c;
> +	}
> +
> +	/* Clear events */
> +	ret = regmap_write(haptics->regmap,
> +			   DA7280_IRQ_EVENT1, events[0]);
> +	if (ret)
> +		goto error_i2c;
> +
> +	return IRQ_HANDLED;
> +
> +error_i2c:
> +	dev_err(haptics->dev, "da7280 i2c error : %d\n", ret);
> +	return IRQ_NONE;
> +}
> +
> +static int da7280_init(struct da7280_haptic *haptics)
> +{
> +	int ret, 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) {
> +		ret = regmap_read(haptics->regmap, DA7280_TOP_CFG1, &val);
> +		if (ret)
> +			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) {
> +			ret = regmap_write(haptics->regmap,
> +					   DA7280_FRQ_LRA_PER_H,
> +					   haptics->resonant_freq_h);
> +			if (ret)
> +				goto error_i2c;
> +			ret = regmap_write(haptics->regmap,
> +					   DA7280_FRQ_LRA_PER_L,
> +					   haptics->resonant_freq_l);
> +			if (ret)
> +				goto error_i2c;
> +		}
> +	} else if (haptics->dev_type == DA7280_ERM_COIN) {
> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_INT_CFG1,
> +					 DA7280_BEMF_FAULT_LIM_MASK, 0);
> +		if (ret)
> +			goto error_i2c;
> +
> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CFG4,
> +					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
> +					 DA7280_V2I_FACTOR_FREEZE_MASK,
> +					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
> +					 DA7280_V2I_FACTOR_FREEZE_MASK);
> +		if (ret)
> +			goto error_i2c;
> +
> +		haptics->acc_en = 0;
> +		haptics->rapid_stop_en = 0;
> +		haptics->amp_pid_en = 0;
> +	}
> +
> +	/* Should be set to 0 only
> +	 * in custom waveform and wideband operation
> +	 */
> +	if (haptics->op_mode >= DA7280_RTWM_MODE)
> +		haptics->bemf_sense_en = 0;
> +
> +	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;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CFG1, mask, val);
> +	if (ret)
> +		goto error_i2c;
> +
> +	if (haptics->nommax != DA7280_SKIP_INIT) {
> +		ret = regmap_write(haptics->regmap,
> +				   DA7280_ACTUATOR1,
> +				   haptics->nommax);
> +		if (ret)
> +			goto error_i2c;
> +	}
> +
> +	if (haptics->absmax != DA7280_SKIP_INIT) {
> +		ret = regmap_write(haptics->regmap, DA7280_ACTUATOR2,
> +				   haptics->absmax);
> +		if (ret)
> +			goto error_i2c;
> +	}
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_ACTUATOR3,
> +				 DA7280_IMAX_MASK,
> +				 haptics->imax);
> +	if (ret)
> +		goto error_i2c;
> +
> +	v2i_factor =
> +		haptics->impd * (haptics->imax + 4) / 1610400;
> +	ret = regmap_write(haptics->regmap,
> +			   DA7280_CALIB_V2I_L,
> +			   v2i_factor & 0xff);
> +	if (ret)
> +		goto error_i2c;
> +	ret = regmap_write(haptics->regmap,
> +			   DA7280_CALIB_V2I_H,
> +			   v2i_factor >> 8);
> +	if (ret)
> +		goto error_i2c;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_TOP_CTL1,
> +				 DA7280_STANDBY_EN_MASK,
> +				 DA7280_STANDBY_EN_MASK);
> +	if (ret)
> +		goto error_i2c;
> +
> +	if (haptics->mem_update) {
> +		ret = da7280_haptic_mem_update(haptics);
> +		if (ret)
> +			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;
> +	ret = regmap_write(haptics->regmap,
> +			   DA7280_SEQ_CTL2, val);
> +	if (ret)
> +		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;
> +		ret = regmap_write(haptics->regmap,
> +				   DA7280_GPI_0_CTL + i, val);
> +		if (ret)
> +			goto error_i2c;
> +	}
> +
> +	/* Clear Interrupts */
> +	ret = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, 0xff);
> +	if (ret)
> +		goto error_i2c;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_IRQ_MASK1,
> +				 DA7280_SEQ_FAULT_M_MASK
> +				 | DA7280_SEQ_DONE_M_MASK, 0);
> +	if (ret)
> +		goto error_i2c;
> +
> +	haptics->suspend_state = 0;

	haptics->suspend_state = false;

> +	return 0;
> +
> +error_i2c:
> +	dev_err(haptics->dev, "haptic init - I2C error : %d\n", ret);
> +	return ret;
> +}
> +
> +/* 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 ret;
> +
> +	if (kstrtol(&buf[0], 0, &val) < 0)
> +		goto err;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_SEQ_CTL2,
> +				 DA7280_PS_SEQ_ID_MASK,
> +				 (val & 0xf) >> DA7280_PS_SEQ_ID_SHIFT);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to update register : %d\n", ret);
> +		return ret;
> +	}
> +
> +	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 ret;
> +	unsigned int val;
> +
> +	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to read register : %d\n", ret);
> +		return ret;
> +	}
> +	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 ret;
> +
> +	if (kstrtol(&buf[0], 0, &val) < 0)
> +		goto err;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_SEQ_CTL2,
> +				 DA7280_PS_SEQ_LOOP_MASK,
> +				 (val & 0xF) << DA7280_PS_SEQ_LOOP_SHIFT);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to update register : %d\n", ret);
> +		return ret;
> +	}
> +
> +	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 ret;
> +	unsigned int val;
> +
> +	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to read register : %d\n", ret);
> +		return ret;
> +	}
> +	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 Y > gpi_seq_id
> + * ex) echo 2 15 > /sys/class/..../gpi_seq_id
> + * 0 <= X < 3, 0<= Y <= 15.
> + */

Sysfs attributes should ideally be one value per file. Sounds like you
want 4 attributes here for individual GPIs?

> +static ssize_t gpi_seq_id_store(struct device *dev,
> +				struct device_attribute *attr,
> +				const char *buf,
> +				size_t count)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	u8 gpi_num = 0xff;
> +	long seq_id = 0xff;
> +	int ret;
> +
> +	if (count < 4)
> +		goto err;
> +
> +	if (buf[0] >= '0')
> +		gpi_num = buf[0] - '0';
> +	else
> +		goto err;
> +
> +	if (buf[1] != ' ')
> +		goto err;
> +
> +	if (kstrtol(&buf[2], 0, &seq_id) < 0)
> +		goto err;
> +
> +	if (gpi_num > 2 || seq_id > 0xf)
> +		goto err;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_GPI_0_CTL + gpi_num,
> +				 DA7280_GPI0_SEQUENCE_ID_MASK,
> +				 seq_id << DA7280_GPI0_SEQUENCE_ID_SHIFT);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to update register : %d\n", ret);
> +		return ret;
> +	}
> +
> +	haptics->gpi_ctl[gpi_num].seq_id = seq_id;
> +
> +	return count;
> +
> +err:
> +	dev_err(dev, "Invalid format or values!\n");
> +	return count;
> +}
> +
> +static ssize_t gpi_seq_id_show(struct device *dev,
> +			       struct device_attribute *attr,
> +			       char *buf)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	int ret;
> +	unsigned int val, i;
> +
> +	for (i = 0; i < 3; i++) {
> +		ret = regmap_read(haptics->regmap,
> +				  DA7280_GPI_0_CTL + i, &val);
> +		if (ret) {
> +			dev_err(haptics->dev,
> +				"failed to read register : %d\n", ret);
> +			return ret;
> +		}
> +		haptics->gpi_ctl[i].seq_id =
> +			(val & DA7280_GPI0_SEQUENCE_ID_MASK)
> +				>> DA7280_GPI0_SEQUENCE_ID_SHIFT;
> +		val = 0;
> +	}
> +
> +	return sprintf(buf,
> +		"Seq ID\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> +		haptics->gpi_ctl[0].seq_id,
> +		haptics->gpi_ctl[1].seq_id,
> +		haptics->gpi_ctl[2].seq_id);
> +}
> +
> +/* Valid format for GPIx_MODE
> + * echo X Y > gpi_mode
> + * ex) echo 2 1 > /sys/class/..../gpi_mode
> + * 0 <= X < 3, 0<= Y <= 1.
> + */
> +static ssize_t gpi_mode_store(struct device *dev,
> +			      struct device_attribute *attr,
> +			      const char *buf, size_t count)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	u8 gpi_num = 0xff, gpi_mode = 0xff;
> +	int ret;
> +
> +	if (count < 3)
> +		goto err;
> +
> +	if (buf[0] >= '0')
> +		gpi_num = buf[0] - '0';
> +	if (buf[2] >= '0')
> +		gpi_mode = buf[2] - '0';
> +
> +	if (gpi_num > 2 || gpi_mode > 1)
> +		goto err;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_GPI_0_CTL + gpi_num,
> +				 DA7280_GPI0_MODE_MASK,
> +				 gpi_mode << DA7280_GPI0_MODE_SHIFT);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to update register : %d\n", ret);
> +		return ret;
> +	}
> +
> +	haptics->gpi_ctl[gpi_num].mode = gpi_mode;
> +
> +	return count;
> +
> +err:
> +	dev_err(dev, "Invalid format!\n");
> +	return count;
> +}
> +
> +static ssize_t gpi_mode_show(struct device *dev,
> +			     struct device_attribute *attr,
> +			     char *buf)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	int ret;
> +	unsigned int val, i;
> +
> +	for (i = 0; i < 3; i++) {
> +		ret = regmap_read(haptics->regmap,
> +				  DA7280_GPI_0_CTL + i, &val);
> +		if (ret) {
> +			dev_err(haptics->dev,
> +				"failed to read register : %d\n", ret);
> +			return ret;
> +		}
> +		haptics->gpi_ctl[i].mode =
> +			(val & DA7280_GPI0_MODE_MASK)
> +				>> DA7280_GPI0_MODE_SHIFT;
> +		val = 0;
> +	}
> +
> +	return sprintf(buf, "Mode\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> +		haptics->gpi_ctl[0].mode,
> +		haptics->gpi_ctl[1].mode,
> +		haptics->gpi_ctl[2].mode);
> +}
> +
> +/* Valid format for GPIx_MODE
> + *  echo X Y > gpi_pol
> + *  ex) echo 2 1 > /sys/class/..../gpi_pol
> + *  0 <= X < 3, 0<= Y <= 2.
> + */
> +static ssize_t gpi_pol_store(struct device *dev,
> +			     struct device_attribute *attr,
> +			     const char *buf,
> +			     size_t count)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	u8 gpi_pol = 0xff, gpi_num = 0xff;
> +	int ret;
> +
> +	if (count < 3)
> +		goto err;
> +
> +	if (buf[0] >= '0')
> +		gpi_num = buf[0] - '0';
> +	if (buf[2] >= '0')
> +		gpi_pol = buf[2] - '0';
> +
> +	if (gpi_num > 2 || gpi_pol > 2)
> +		goto err;
> +
> +	ret = regmap_update_bits(haptics->regmap,
> +				 DA7280_GPI_0_CTL + gpi_num,
> +				 DA7280_GPI0_POLARITY_MASK,
> +				 gpi_pol << DA7280_GPI0_POLARITY_SHIFT);
> +	if (ret) {
> +		dev_err(haptics->dev,
> +			"failed to update register : %d\n", ret);
> +		return ret;
> +	}
> +
> +	haptics->gpi_ctl[gpi_num].polarity = gpi_pol;
> +
> +	return count;
> +
> +err:
> +	dev_err(dev, "Invalid format or input values!\n");
> +	return count;
> +}
> +
> +static ssize_t gpi_pol_show(struct device *dev,
> +			    struct device_attribute *attr,
> +			    char *buf)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	int ret = 0;
> +	unsigned int val, i;
> +
> +	for (i = 0; i < 3; i++) {
> +		ret = regmap_read(haptics->regmap,
> +				  DA7280_GPI_0_CTL + i, &val);
> +		if (ret)
> +			return ret;
> +		haptics->gpi_ctl[i].polarity =
> +			(val & DA7280_GPI0_POLARITY_MASK)
> +			>> DA7280_GPI0_POLARITY_SHIFT;
> +		val = 0;
> +	}
> +
> +	return sprintf(buf, "Polarity\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
> +		haptics->gpi_ctl[0].polarity,
> +		haptics->gpi_ctl[1].polarity,
> +		haptics->gpi_ctl[2].polarity);
> +}
> +
> +#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 ret;
> +
> +	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
> +	if (ret)
> +		return ret;
> +
> +	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);
> +
> +	ret = da7280_haptic_mem_update(haptics);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR_RW(ps_seq_id);
> +static DEVICE_ATTR_RW(ps_seq_loop);
> +static DEVICE_ATTR_RW(gpi_seq_id);
> +static DEVICE_ATTR_RW(gpi_mode);
> +static DEVICE_ATTR_RW(gpi_pol);
> +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_id.attr,
> +	&dev_attr_gpi_mode.attr,
> +	&dev_attr_gpi_pol.attr,
> +	&dev_attr_patterns.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group da7280_attr_group = {
> +	.attrs = da7280_sysfs_attr,
> +};
> +
> +static const struct attribute_group *da7280_attr_groups[] = {
> +	&da7280_attr_group,
> +	NULL,
> +};
> +
> +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 ret;

I'd prefer if we called this "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;
> +	}
> +
> +	/* Handle DT data if provided */
> +	if (client->dev.of_node)
> +		da7280_of_to_pdata(&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)) {
> +		ret = PTR_ERR(haptics->regmap);
> +		dev_err(dev, "Failed to allocate register map : %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, client->irq, NULL,
> +					da7280_irq_handler,
> +					IRQF_TRIGGER_LOW | IRQF_ONESHOT,

Do not hard-code trigger, let platform set it up as needed. Use
IRQF_ONESHOT only.

> +					"da7280-haptics", haptics);
> +	if (ret != 0) {
> +		dev_err(dev,
> +			"Failed to request IRQ : %d\n", client->irq);
> +		return ret;
> +	}
> +
> +	ret = da7280_init(haptics);
> +	if (ret) {
> +		dev_err(dev, "failed to initialize device\n");
> +		return ret;
> +	}
> +
> +	/* 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);
> +
> +	ret = input_ff_create_memless(haptics->input_dev, NULL,
> +				      da7280_haptic_play);

The device seems to be able to load a custom waveform into memory, why
do you use input_ff_create_memless() instead if setting it up as
FF_CUSTOM?

> +	if (ret) {
> +		dev_err(dev, "failed to create force-feedback\n");
> +		return ret;
> +	}
> +
> +#ifdef CONFIG_SYSFS
> +	haptics->input_dev->dev.groups = da7280_attr_groups;

These properties control behavior of hardware device, not input device
abstraction, so they should be attached to the platform device.

Please use devm_device_add_group() or devm_device_add_groups().

> +#endif
> +
> +	ret = input_register_device(haptics->input_dev);
> +	if (ret)
> +		dev_err(dev, "failed to register input device\n");
> +
> +	return ret;
> +}
> +
> +static int __maybe_unused da7280_suspend(struct device *dev)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&haptics->input_dev->mutex);



> +	if (haptics->suspend_state == 0) {

	if (!haptics->suspend_state) ...

But how exactly can we get here if we already in suspend state?

Also, maybe we shoudl check if device is in fact opened by anyone?

Also, I think you should cancel outstanding work, if any.

> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CTL1,
> +					 DA7280_STANDBY_EN_MASK, 0);
> +		if (ret)
> +			dev_err(haptics->dev,
> +				"I2C error : %d\n", ret);
> +		else
> +			haptics->suspend_state = 1;

						= true;

> +	}
> +	mutex_unlock(&haptics->input_dev->mutex);
> +	return ret;
> +}
> +
> +static int __maybe_unused da7280_resume(struct device *dev)
> +{
> +	struct da7280_haptic *haptics = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&haptics->input_dev->mutex);
> +	if (haptics->suspend_state) {

How can we get here if we are not in suspend state.

> +		ret = regmap_update_bits(haptics->regmap,
> +					 DA7280_TOP_CTL1,
> +					 DA7280_STANDBY_EN_MASK,
> +					 DA7280_STANDBY_EN_MASK);
> +		if (ret)
> +			dev_err(haptics->dev,
> +				"i2c error : %d\n", ret);
> +		else
> +			haptics->suspend_state = 0;

			= false;

> +	}
> +	mutex_unlock(&haptics->input_dev->mutex);
> +	return ret;
> +}
> +
> +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)

I do not think these definitions are of interest to anyone outside the
driver itself, why don't you keep them in the .c file?

> +
> +#endif
> -- 
> end-of-patch for RESEND PATCH V6
> 

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [RFC/PATCH 0/5] Support children for legacy device properties
From: Linus Walleij @ 2018-09-19 19:55 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Rafael J. Wysocki, Linux Input, open list:GPIO SUBSYSTEM,
	linux-kernel@vger.kernel.org, Andy Shevchenko
In-Reply-To: <20180917181603.125492-1-dmitry.torokhov@gmail.com>

On Mon, Sep 17, 2018 at 11:16 AM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:

> The generic device properties APIs are very helpful as they allow abstracting
> away details of the platform (whether it is ACPI, device tree, or legacy board
> file), so that individual driver does not need separate code paths to support
> all variants. However there are drivers that currently can not use generic
> device properties API as they need notion of children properties, for example
> gpio_keys driver, that expects every button to be described as a sub-node of
> main device.
>
> This patch series introduces notion of sub-nodes for static properties and ties
> it up with GPIO lookup tables so that they are usable with sub-nodes as well.

This is the patch series I would have written, had I been smart enough.
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
for the series.

I can't test the SIM.ONE board with this until next week but the approach
is definately what we want, not just for legacy boards, but also for any
other non-discoverable hardware we currently poke into
drivers/platform or arch/x86/platform etc.

Yours,
Linus Walleij

Yours,
Linus Walleij

^ permalink raw reply

* Re: [RFC/PATCH 2/5] device property: introduce notion of subnodes for legacy boards
From: Dmitry Torokhov @ 2018-09-19 17:13 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180919151026.GB11965@kuha.fi.intel.com>

Hi Heikki,

On Wed, Sep 19, 2018 at 06:10:26PM +0300, Heikki Krogerus wrote:
> Hi,
> 
> On Mon, Sep 17, 2018 at 11:16:00AM -0700, Dmitry Torokhov wrote:
> > Several drivers rely on having notion of sub-nodes when describing
> > hardware, let's allow static board-defined properties also have it.
> > 
> > The board files will then attach properties to devices in the following
> > fashion:
> > 
> > 	device_add_properties(&board_platform_device.dev,
> > 			      main_device_props);
> > 	device_add_child_properties(&board_platform_device.dev,
> > 				    dev_fwnode(&board_platform_device.dev),
> > 				    child1_device_props);
> > 	device_add_child_properties(&board_platform_device.dev,
> > 				    dev_fwnode(&board_platform_device.dev),
> > 				    child2_device_props);
> > 	...
> > 	platform_device_register(&board_platform_device.dev);
> > 
> > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > ---
> >  drivers/base/pset_property.c | 110 +++++++++++++++++++++++++++++++----
> >  include/linux/property.h     |   4 ++
> >  2 files changed, 102 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/base/pset_property.c b/drivers/base/pset_property.c
> > index 08ecc13080ae..63f2377aefe8 100644
> > --- a/drivers/base/pset_property.c
> > +++ b/drivers/base/pset_property.c
> > @@ -18,6 +18,11 @@ struct property_set {
> >  	struct device *dev;
> >  	struct fwnode_handle fwnode;
> >  	const struct property_entry *properties;
> > +
> > +	struct property_set *parent;
> > +	/* Entry in parent->children list */
> > +	struct list_head child_node;
> > +	struct list_head children;
> 
> Add
> 
>         const char *name;
> 
> and you can implement also pset_get_named_child_node().

Or
	char name[];

to avoid separate allocation.

Alternatively, we can add it later when we need it, and add
device_add_named_child_properties().

I'll leave it up to Rafael to decide.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 01/20] Input: iforce - remove "being used" silliness
From: Dmitry Torokhov @ 2018-09-19 17:10 UTC (permalink / raw)
  To: Tim Schumacher; +Cc: linux-input, linux-kernel
In-Reply-To: <0541f46d-1f4f-05f4-8e93-6663f6c13bd9@gmx.de>

On Wed, Sep 19, 2018 at 04:51:26PM +0200, Tim Schumacher wrote:
> On 18.09.18 02:47, Dmitry Torokhov wrote:
> > The kernel is supposed to handle multiple devices, static flags
> > in packet handling code will never work.
> > 
> > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > ---
> > 
> > This is a random assortment of iforce patches that I made a few weeks back.
> > 
> > Tim, I do not have hardware, so I was bound to screw it up, but if you
> > have some time I'd appreciate if you try them out (and if I indeed broke
> > things if you could identify issues that would be even more awesome).
> > 
> > Thanks!
> > 
> Hello Dmitry,
> 
> I tested those patches and I didn't find any obvious issues. The basic functions
> do work (i.e. buttons and axes, don't have a HAT so I can't test that), force
> feedback seems to work to the extent it was before (I only have fftest though,
> no games that support force feedback). I'll go through a few more applications
> and see if anything not obvious is broken.
> 

Thank you for taking a look.

> Unfortunately, I only have that one wheel and I can only test USB connections
> at the moment (unless I find a proper serial adaptor, but I'm not sure if that
> would even work).
> 
> Are those patches planned to go into 4.19 or are they intended to be merged in
> the next development cycle?

Definitely not 4.19. Could be 4.20 or 4.21...

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [RFC/PATCH 2/5] device property: introduce notion of subnodes for legacy boards
From: Heikki Krogerus @ 2018-09-19 15:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180917181603.125492-3-dmitry.torokhov@gmail.com>

Hi,

On Mon, Sep 17, 2018 at 11:16:00AM -0700, Dmitry Torokhov wrote:
> Several drivers rely on having notion of sub-nodes when describing
> hardware, let's allow static board-defined properties also have it.
> 
> The board files will then attach properties to devices in the following
> fashion:
> 
> 	device_add_properties(&board_platform_device.dev,
> 			      main_device_props);
> 	device_add_child_properties(&board_platform_device.dev,
> 				    dev_fwnode(&board_platform_device.dev),
> 				    child1_device_props);
> 	device_add_child_properties(&board_platform_device.dev,
> 				    dev_fwnode(&board_platform_device.dev),
> 				    child2_device_props);
> 	...
> 	platform_device_register(&board_platform_device.dev);
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
>  drivers/base/pset_property.c | 110 +++++++++++++++++++++++++++++++----
>  include/linux/property.h     |   4 ++
>  2 files changed, 102 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/base/pset_property.c b/drivers/base/pset_property.c
> index 08ecc13080ae..63f2377aefe8 100644
> --- a/drivers/base/pset_property.c
> +++ b/drivers/base/pset_property.c
> @@ -18,6 +18,11 @@ struct property_set {
>  	struct device *dev;
>  	struct fwnode_handle fwnode;
>  	const struct property_entry *properties;
> +
> +	struct property_set *parent;
> +	/* Entry in parent->children list */
> +	struct list_head child_node;
> +	struct list_head children;

Add

        const char *name;

and you can implement also pset_get_named_child_node().

>  };
>  
>  static const struct fwnode_operations pset_fwnode_ops;
> @@ -283,10 +288,47 @@ pset_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
>  					   val, nval);
>  }
>  
> +struct fwnode_handle *pset_fwnode_get_parent(const struct fwnode_handle *fwnode)
> +{
> +	struct property_set *pset = to_pset_node(fwnode);
> +
> +	return pset ? &pset->parent->fwnode : NULL;
> +}
> +
> +struct fwnode_handle *
> +pset_fwnode_get_next_subnode(const struct fwnode_handle *fwnode,
> +			     struct fwnode_handle *child)
> +{
> +	const struct property_set *pset = to_pset_node(fwnode);
> +	struct property_set *first_child;
> +	struct property_set *next;
> +
> +	if (!pset)
> +		return NULL;
> +
> +	if (list_empty(&pset->children))
> +		return NULL;
> +
> +	first_child = list_first_entry(&pset->children, struct property_set,
> +				       child_node);
> +
> +	if (child) {
> +		next = list_next_entry(to_pset_node(child), child_node);
> +		if (next == first_child)
> +			return NULL;
> +	} else {
> +		next = first_child;
> +	}
> +
> +	return &next->fwnode;
> +}
> +
>  static const struct fwnode_operations pset_fwnode_ops = {
>  	.property_present = pset_fwnode_property_present,
>  	.property_read_int_array = pset_fwnode_read_int_array,
>  	.property_read_string_array = pset_fwnode_property_read_string_array,
> +	.get_parent = pset_fwnode_get_parent,
> +	.get_next_child_node = pset_fwnode_get_next_subnode,
>  };
>  
>  static void property_entry_free_data(const struct property_entry *p)
> @@ -439,24 +481,31 @@ EXPORT_SYMBOL_GPL(property_entries_free);
>   */
>  static void pset_free_set(struct property_set *pset)
>  {
> +	struct property_set *child, *next;
> +
>  	if (!pset)
>  		return;
>  
> +	list_for_each_entry_safe(child, next, &pset->children, child_node) {
> +		list_del(&child->child_node);
> +		pset_free_set(child);
> +	}
> +
>  	property_entries_free(pset->properties);
>  	kfree(pset);
>  }
>  
>  /**
> - * pset_copy_set - copies property set
> - * @pset: Property set to copy
> + * pset_create_set - creates property set.
> + * @src: property entries for the set.
>   *
> - * This function takes a deep copy of the given property set and returns
> - * pointer to the copy. Call device_free_property_set() to free resources
> - * allocated in this function.
> + * This function takes a deep copy of the given property entries and creates
> + * property set. Call pset_free_set() to free resources allocated in this
> + * function.
>   *
>   * Return: Pointer to the new property set or error pointer.
>   */
> -static struct property_set *pset_copy_set(const struct property_set *pset)
> +static struct property_set *pset_create_set(const struct property_entry *src)
>  {
>  	struct property_entry *properties;
>  	struct property_set *p;
> @@ -465,7 +514,11 @@ static struct property_set *pset_copy_set(const struct property_set *pset)
>  	if (!p)
>  		return ERR_PTR(-ENOMEM);
>  
> -	properties = property_entries_dup(pset->properties);
> +	INIT_LIST_HEAD(&p->child_node);
> +	INIT_LIST_HEAD(&p->children);
> +	p->fwnode.ops = &pset_fwnode_ops;
> +
> +	properties = property_entries_dup(src);
>  	if (IS_ERR(properties)) {
>  		kfree(p);
>  		return ERR_CAST(properties);
> @@ -521,20 +574,53 @@ EXPORT_SYMBOL_GPL(device_remove_properties);
>  int device_add_properties(struct device *dev,
>  			  const struct property_entry *properties)
>  {
> -	struct property_set *p, pset;
> +	struct property_set *p;
>  
>  	if (!properties)
>  		return -EINVAL;
>  
> -	pset.properties = properties;
> -
> -	p = pset_copy_set(&pset);
> +	p = pset_create_set(properties);
>  	if (IS_ERR(p))
>  		return PTR_ERR(p);
>  
> -	p->fwnode.ops = &pset_fwnode_ops;
>  	set_secondary_fwnode(dev, &p->fwnode);
>  	p->dev = dev;
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(device_add_properties);
> +
> +/**
> + * device_add_child_properties - Add a collection of properties to a device object.
> + * @dev: Device to add properties to.

@parent: missing

> + * @properties: Collection of properties to add.
> + *
> + * Associate a collection of device properties represented by @properties as a
> + * child of given @parent firmware node.  The function takes a copy of
> + * @properties.
> + */
> +struct fwnode_handle *
> +device_add_child_properties(struct device *dev,

device_add_child_properties(struct device *dev, const char *child_name,

> +			    struct fwnode_handle *parent,
> +			    const struct property_entry *properties)
> +{
> +	struct property_set *p;
> +	struct property_set *parent_pset;
> +
> +	if (!properties)
> +		return ERR_PTR(-EINVAL);
> +
> +	parent_pset = to_pset_node(parent);
> +	if (!parent_pset)
> +		return ERR_PTR(-EINVAL);
> +
> +	p = pset_create_set(properties);
> +	if (IS_ERR(p))
> +		return ERR_CAST(p);
> +
> +	p->dev = dev;

        p->name = kstrdup_const(child_name, GFP_KERNEL);

You'll need to kfree_const(pset->name) in pset_free_set() of course.

> +	p->parent = parent_pset;
> +	list_add_tail(&p->child_node, &parent_pset->children);
> +
> +	return &p->fwnode;
> +}
> +EXPORT_SYMBOL_GPL(device_add_child_properties);


Br,

-- 
heikki

^ permalink raw reply

* Re: [PATCH 01/20] Input: iforce - remove "being used" silliness
From: Tim Schumacher @ 2018-09-19 14:51 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel
In-Reply-To: <20180918004732.9875-1-dmitry.torokhov@gmail.com>

On 18.09.18 02:47, Dmitry Torokhov wrote:
> The kernel is supposed to handle multiple devices, static flags
> in packet handling code will never work.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
> 
> This is a random assortment of iforce patches that I made a few weeks back.
> 
> Tim, I do not have hardware, so I was bound to screw it up, but if you
> have some time I'd appreciate if you try them out (and if I indeed broke
> things if you could identify issues that would be even more awesome).
> 
> Thanks!
> 
Hello Dmitry,

I tested those patches and I didn't find any obvious issues. The basic functions
do work (i.e. buttons and axes, don't have a HAT so I can't test that), force
feedback seems to work to the extent it was before (I only have fftest though,
no games that support force feedback). I'll go through a few more applications
and see if anything not obvious is broken.

Unfortunately, I only have that one wheel and I can only test USB connections
at the moment (unless I find a proper serial adaptor, but I'm not sure if that
would even work).

Are those patches planned to go into 4.19 or are they intended to be merged in
the next development cycle?

^ permalink raw reply

* [RESEND PATCH V6 1/3] MAINTAINERS: da7280 updates to the Dialog Semiconductor search terms
From: Roy Im @ 2018-09-19 10:35 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1537353304.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>

---
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 d870cb5..6244a7d 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 V6

^ permalink raw reply related

* [RESEND PATCH V6 3/3] Input: new da7280 haptic driver
From: Roy Im @ 2018-09-19 10:35 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1537353304.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>

---
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 | 1438 +++++++++++++++++++++++++++++++++++++++++++
 drivers/input/misc/da7280.h |  412 +++++++++++++
 4 files changed, 1864 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..041a9f4
--- /dev/null
+++ b/drivers/input/misc/da7280.c
@@ -0,0 +1,1438 @@
+// 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;
+
+	bool suspend_state;
+	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;
+	u8 bemf_sense_en;
+	u8 freq_track_en;
+	u8 acc_en;
+	u8 rapid_stop_en;
+	u8 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];
+	const struct attribute_group **attr_group;
+};
+
+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 ret;
+	unsigned int val;
+
+	/* It is recommended to update the patterns
+	 * during haptic is not working in order to avoid conflict
+	 */
+	ret = regmap_read(haptics->regmap, DA7280_IRQ_STATUS1, &val);
+	if (ret)
+		return ret;
+	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;
+	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL2, &val);
+	if (ret)
+		return ret;
+	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 */
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_OPERATION_MODE_MASK,
+				 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
+	if (ret)
+		return ret;
+
+	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 ret;
+
+	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;
+
+	ret = pwm_config(haptics->pwm_dev,
+			 pwm_duty, pargs.period);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to configure pwm : %d\n", ret);
+		return ret;
+	}
+
+	ret = pwm_enable(haptics->pwm_dev);
+	if (ret) {
+		pwm_disable(haptics->pwm_dev);
+		dev_err(haptics->dev,
+			"failed to enable haptics pwm device : %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void da7280_haptic_enable(struct da7280_haptic *haptics)
+{
+	int ret = 0;
+
+	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)
+		 */
+		ret = regmap_write(haptics->regmap,
+				   DA7280_TOP_CTL2,
+				   haptics->magnitude);
+		if (ret) {
+			dev_err(haptics->dev,
+				"i2c err for driving level set : %d\n",
+				ret);
+			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;
+	}
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_OPERATION_MODE_MASK,
+				 haptics->op_mode);
+	if (ret) {
+		dev_err(haptics->dev,
+			"i2c err for op_mode setting : %d\n", ret);
+		return;
+	}
+
+	if (haptics->op_mode == DA7280_PWM_MODE ||
+	    haptics->op_mode == DA7280_RTWM_MODE) {
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CTL1,
+					 DA7280_SEQ_START_MASK,
+					 DA7280_SEQ_START_MASK);
+		if (ret)
+			dev_err(haptics->dev,
+				"i2c err for sequence triggering : %d\n", ret);
+	}
+}
+
+static void da7280_haptic_disable(struct da7280_haptic *haptics)
+{
+	int ret;
+
+	/* Set to Inactive mode */
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_OPERATION_MODE_MASK, 0);
+	if (ret) {
+		dev_err(haptics->dev,
+			"i2c err for op_mode off : %d\n", ret);
+		return;
+	}
+
+	switch (haptics->op_mode) {
+	case DA7280_RTWM_MODE:
+	case DA7280_ETWM_MODE:
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CTL1,
+					 DA7280_SEQ_START_MASK, 0);
+		if (ret) {
+			dev_err(haptics->dev,
+				"i2c err for RTWM or ETWM mode off : %d\n",
+				ret);
+			return;
+		}
+		break;
+	case DA7280_DRO_MODE:
+		ret = regmap_write(haptics->regmap,
+				   DA7280_TOP_CTL2, 0);
+		if (ret) {
+			dev_err(haptics->dev,
+				"i2c err for DRO mode off : %d\n",
+				ret);
+			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;
+	}
+}
+
+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 ret;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_STANDBY_EN_MASK,
+				 DA7280_STANDBY_EN_MASK);
+	if (ret)
+		dev_err(haptics->dev,
+			"Failed to open haptic, i2c error : %d\n", ret);
+
+	return ret;
+}
+
+static void da7280_haptic_close(struct input_dev *dev)
+{
+	struct da7280_haptic *haptics = input_get_drvdata(dev);
+	int ret;
+
+	cancel_work_sync(&haptics->work);
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_OPERATION_MODE_MASK, 0);
+	if (ret)
+		goto error_i2c;
+
+	if (haptics->op_mode == DA7280_DRO_MODE) {
+		ret = regmap_write(haptics->regmap,
+				   DA7280_TOP_CTL2, 0);
+
+		if (ret)
+			goto error_i2c;
+	}
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_STANDBY_EN_MASK, 0);
+	if (ret)
+		goto error_i2c;
+
+	return;
+
+error_i2c:
+	dev_err(haptics->dev, "DA7280-haptic i2c error : %d\n", ret);
+}
+
+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_of_to_pdata(struct device *dev,
+			       struct da7280_haptic *haptics)
+{
+	struct device_node *np = dev->of_node;
+	char dt_gpi_str1[] = "dlg,gpi0-seq-id";
+	char dt_gpi_str2[] = "dlg,gpi0-mode";
+	char dt_gpi_str3[] = "dlg,gpi0-polarity";
+	unsigned int mem[DA7280_SNP_MEM_SIZE];
+	const char *of_str;
+	u32 of_val32;
+	int i;
+
+	if (!of_property_read_string(np, "dlg,actuator-type", &of_str))
+		haptics->dev_type =
+			da7280_haptic_of_mode_str(dev, of_str);
+	else /* if no property, then use the mode inside chip */
+		haptics->dev_type = DA7280_DEV_MAX;
+
+	if (of_property_read_u32(np, "dlg,op-mode", &of_val32) >= 0)
+		if (of_val32 && of_val32 < DA7280_OPMODE_MAX)
+			haptics->op_mode = of_val32;
+		else
+			haptics->op_mode = DA7280_DRO_MODE;
+	else
+		haptics->op_mode = DA7280_DRO_MODE;
+
+	if (of_property_read_u32(np, "dlg,nom-microvolt", &of_val32) >= 0)
+		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
+			haptics->nommax =
+				da7280_haptic_of_volt_rating_set(of_val32);
+		else
+			haptics->nommax = DA7280_SKIP_INIT;
+	else /* if no property, then use the value inside chip */
+		haptics->nommax = DA7280_SKIP_INIT;
+
+	if (of_property_read_u32(np, "dlg,abs-max-microvolt", &of_val32) >= 0)
+		if (of_val32 < DA7280_VOLTAGE_RATE_MAX)
+			haptics->absmax =
+				da7280_haptic_of_volt_rating_set(of_val32);
+		else
+			haptics->absmax = DA7280_SKIP_INIT;
+	else
+		haptics->absmax = DA7280_SKIP_INIT;
+
+	if (of_property_read_u32(np, "dlg,imax-microamp", &of_val32) >= 0)
+		if (of_val32 < DA7280_IMAX_LIMIT)
+			haptics->imax = (of_val32 - 28600)
+					/ DA7280_IMAX_STEP + 1;
+		else
+			haptics->imax = DA7280_IMAX_DEFAULT;
+	else
+		haptics->imax = DA7280_IMAX_DEFAULT;
+
+	if (of_property_read_u32(np, "dlg,impd-micro-ohms", &of_val32) >= 0)
+		if (of_val32 <= DA7280_IMPD_MAX)
+			haptics->impd = of_val32;
+		else
+			haptics->impd = DA7280_IMPD_DEFAULT;
+	else
+		haptics->impd = DA7280_IMPD_DEFAULT;
+
+	if (of_property_read_u32(np, "dlg,resonant-freq-hz", &of_val32) >= 0) {
+		if (of_val32 < DA7280_MAX_RESONAT_FREQ_HZ &&
+		    of_val32 > DA7280_MIN_RESONAT_FREQ_HZ) {
+			haptics->resonant_freq_h =
+				((1000000000 / (of_val32 * 1333)) >> 7) & 0xFF;
+			haptics->resonant_freq_l =
+				(1000000000 / (of_val32 * 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 (of_property_read_u32(np, "dlg,ps-seq-id", &of_val32) >= 0)
+		if (of_val32 <= DA7280_SEQ_ID_MAX)
+			haptics->ps_seq_id = of_val32;
+		else
+			haptics->ps_seq_id = 0;
+	else /* if no property, set to zero as a default do nothing */
+		haptics->ps_seq_id = 0;
+
+	if (of_property_read_u32(np, "dlg,ps-seq-loop", &of_val32) >= 0)
+		if (of_val32 <= DA7280_SEQ_LOOP_MAX)
+			haptics->ps_seq_loop = of_val32;
+		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++) {
+		dt_gpi_str1[7] = '0' + i;
+		if (of_property_read_u32(np, dt_gpi_str1, &of_val32) >= 0)
+			if (of_val32 <= DA7280_SEQ_ID_MAX)
+				haptics->gpi_ctl[i].seq_id = of_val32;
+			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;
+
+		dt_gpi_str2[7] = '0' + i;
+		if (!of_property_read_string(np, dt_gpi_str2, &of_str))
+			haptics->gpi_ctl[i].mode =
+				da7280_haptic_of_gpi_mode_str(dev, of_str);
+		else
+			haptics->gpi_ctl[i].mode = 0;
+
+		dt_gpi_str3[7] = '0' + i;
+		if (!of_property_read_string(np, dt_gpi_str3, &of_str))
+			haptics->gpi_ctl[i].polarity =
+				da7280_haptic_of_gpi_pol_str(dev, of_str);
+		else
+			haptics->gpi_ctl[i].polarity = 0;
+	}
+
+	haptics->bemf_sense_en =
+		of_property_read_bool(np, "dlg,bemf-sens-enable");
+	haptics->freq_track_en =
+		of_property_read_bool(np, "dlg,freq-track-enable");
+	haptics->acc_en =
+		of_property_read_bool(np, "dlg,acc-enable");
+	haptics->rapid_stop_en =
+		of_property_read_bool(np, "dlg,rapid-stop-enable");
+	haptics->amp_pid_en =
+		of_property_read_bool(np, "dlg,amp-pid-enable");
+
+	if (of_property_read_u32_array(np, "dlg,mem-array",
+				       &mem[0], DA7280_SNP_MEM_SIZE) >= 0) {
+		haptics->mem_update = 1;
+		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 = 0;
+	}
+}
+
+static irqreturn_t da7280_irq_handler(int irq, void *data)
+{
+	struct da7280_haptic *haptics = data;
+	u8 events[IRQ_NUM];
+	int ret;
+
+	/* Check what events have happened */
+	ret = regmap_bulk_read(haptics->regmap,
+			       DA7280_IRQ_EVENT1,
+			       events, IRQ_NUM);
+	if (ret)
+		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.
+		 */
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CTL1,
+					 DA7280_OPERATION_MODE_MASK, 0);
+		if (ret)
+			goto error_i2c;
+	}
+
+	/* Clear events */
+	ret = regmap_write(haptics->regmap,
+			   DA7280_IRQ_EVENT1, events[0]);
+	if (ret)
+		goto error_i2c;
+
+	return IRQ_HANDLED;
+
+error_i2c:
+	dev_err(haptics->dev, "da7280 i2c error : %d\n", ret);
+	return IRQ_NONE;
+}
+
+static int da7280_init(struct da7280_haptic *haptics)
+{
+	int ret, 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) {
+		ret = regmap_read(haptics->regmap, DA7280_TOP_CFG1, &val);
+		if (ret)
+			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) {
+			ret = regmap_write(haptics->regmap,
+					   DA7280_FRQ_LRA_PER_H,
+					   haptics->resonant_freq_h);
+			if (ret)
+				goto error_i2c;
+			ret = regmap_write(haptics->regmap,
+					   DA7280_FRQ_LRA_PER_L,
+					   haptics->resonant_freq_l);
+			if (ret)
+				goto error_i2c;
+		}
+	} else if (haptics->dev_type == DA7280_ERM_COIN) {
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_INT_CFG1,
+					 DA7280_BEMF_FAULT_LIM_MASK, 0);
+		if (ret)
+			goto error_i2c;
+
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CFG4,
+					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+					 DA7280_V2I_FACTOR_FREEZE_MASK,
+					 DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+					 DA7280_V2I_FACTOR_FREEZE_MASK);
+		if (ret)
+			goto error_i2c;
+
+		haptics->acc_en = 0;
+		haptics->rapid_stop_en = 0;
+		haptics->amp_pid_en = 0;
+	}
+
+	/* Should be set to 0 only
+	 * in custom waveform and wideband operation
+	 */
+	if (haptics->op_mode >= DA7280_RTWM_MODE)
+		haptics->bemf_sense_en = 0;
+
+	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;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CFG1, mask, val);
+	if (ret)
+		goto error_i2c;
+
+	if (haptics->nommax != DA7280_SKIP_INIT) {
+		ret = regmap_write(haptics->regmap,
+				   DA7280_ACTUATOR1,
+				   haptics->nommax);
+		if (ret)
+			goto error_i2c;
+	}
+
+	if (haptics->absmax != DA7280_SKIP_INIT) {
+		ret = regmap_write(haptics->regmap, DA7280_ACTUATOR2,
+				   haptics->absmax);
+		if (ret)
+			goto error_i2c;
+	}
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_ACTUATOR3,
+				 DA7280_IMAX_MASK,
+				 haptics->imax);
+	if (ret)
+		goto error_i2c;
+
+	v2i_factor =
+		haptics->impd * (haptics->imax + 4) / 1610400;
+	ret = regmap_write(haptics->regmap,
+			   DA7280_CALIB_V2I_L,
+			   v2i_factor & 0xff);
+	if (ret)
+		goto error_i2c;
+	ret = regmap_write(haptics->regmap,
+			   DA7280_CALIB_V2I_H,
+			   v2i_factor >> 8);
+	if (ret)
+		goto error_i2c;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_TOP_CTL1,
+				 DA7280_STANDBY_EN_MASK,
+				 DA7280_STANDBY_EN_MASK);
+	if (ret)
+		goto error_i2c;
+
+	if (haptics->mem_update) {
+		ret = da7280_haptic_mem_update(haptics);
+		if (ret)
+			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;
+	ret = regmap_write(haptics->regmap,
+			   DA7280_SEQ_CTL2, val);
+	if (ret)
+		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;
+		ret = regmap_write(haptics->regmap,
+				   DA7280_GPI_0_CTL + i, val);
+		if (ret)
+			goto error_i2c;
+	}
+
+	/* Clear Interrupts */
+	ret = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, 0xff);
+	if (ret)
+		goto error_i2c;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_IRQ_MASK1,
+				 DA7280_SEQ_FAULT_M_MASK
+				 | DA7280_SEQ_DONE_M_MASK, 0);
+	if (ret)
+		goto error_i2c;
+
+	haptics->suspend_state = 0;
+	return 0;
+
+error_i2c:
+	dev_err(haptics->dev, "haptic init - I2C error : %d\n", ret);
+	return ret;
+}
+
+/* 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 ret;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_SEQ_CTL2,
+				 DA7280_PS_SEQ_ID_MASK,
+				 (val & 0xf) >> DA7280_PS_SEQ_ID_SHIFT);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", ret);
+		return ret;
+	}
+
+	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 ret;
+	unsigned int val;
+
+	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", ret);
+		return ret;
+	}
+	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 ret;
+
+	if (kstrtol(&buf[0], 0, &val) < 0)
+		goto err;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_SEQ_CTL2,
+				 DA7280_PS_SEQ_LOOP_MASK,
+				 (val & 0xF) << DA7280_PS_SEQ_LOOP_SHIFT);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", ret);
+		return ret;
+	}
+
+	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 ret;
+	unsigned int val;
+
+	ret = regmap_read(haptics->regmap, DA7280_SEQ_CTL2, &val);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to read register : %d\n", ret);
+		return ret;
+	}
+	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 Y > gpi_seq_id
+ * ex) echo 2 15 > /sys/class/..../gpi_seq_id
+ * 0 <= X < 3, 0<= Y <= 15.
+ */
+static ssize_t gpi_seq_id_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	u8 gpi_num = 0xff;
+	long seq_id = 0xff;
+	int ret;
+
+	if (count < 4)
+		goto err;
+
+	if (buf[0] >= '0')
+		gpi_num = buf[0] - '0';
+	else
+		goto err;
+
+	if (buf[1] != ' ')
+		goto err;
+
+	if (kstrtol(&buf[2], 0, &seq_id) < 0)
+		goto err;
+
+	if (gpi_num > 2 || seq_id > 0xf)
+		goto err;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_GPI_0_CTL + gpi_num,
+				 DA7280_GPI0_SEQUENCE_ID_MASK,
+				 seq_id << DA7280_GPI0_SEQUENCE_ID_SHIFT);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", ret);
+		return ret;
+	}
+
+	haptics->gpi_ctl[gpi_num].seq_id = seq_id;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid format or values!\n");
+	return count;
+}
+
+static ssize_t gpi_seq_id_show(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int ret;
+	unsigned int val, i;
+
+	for (i = 0; i < 3; i++) {
+		ret = regmap_read(haptics->regmap,
+				  DA7280_GPI_0_CTL + i, &val);
+		if (ret) {
+			dev_err(haptics->dev,
+				"failed to read register : %d\n", ret);
+			return ret;
+		}
+		haptics->gpi_ctl[i].seq_id =
+			(val & DA7280_GPI0_SEQUENCE_ID_MASK)
+				>> DA7280_GPI0_SEQUENCE_ID_SHIFT;
+		val = 0;
+	}
+
+	return sprintf(buf,
+		"Seq ID\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
+		haptics->gpi_ctl[0].seq_id,
+		haptics->gpi_ctl[1].seq_id,
+		haptics->gpi_ctl[2].seq_id);
+}
+
+/* Valid format for GPIx_MODE
+ * echo X Y > gpi_mode
+ * ex) echo 2 1 > /sys/class/..../gpi_mode
+ * 0 <= X < 3, 0<= Y <= 1.
+ */
+static ssize_t gpi_mode_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	u8 gpi_num = 0xff, gpi_mode = 0xff;
+	int ret;
+
+	if (count < 3)
+		goto err;
+
+	if (buf[0] >= '0')
+		gpi_num = buf[0] - '0';
+	if (buf[2] >= '0')
+		gpi_mode = buf[2] - '0';
+
+	if (gpi_num > 2 || gpi_mode > 1)
+		goto err;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_GPI_0_CTL + gpi_num,
+				 DA7280_GPI0_MODE_MASK,
+				 gpi_mode << DA7280_GPI0_MODE_SHIFT);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", ret);
+		return ret;
+	}
+
+	haptics->gpi_ctl[gpi_num].mode = gpi_mode;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid format!\n");
+	return count;
+}
+
+static ssize_t gpi_mode_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int ret;
+	unsigned int val, i;
+
+	for (i = 0; i < 3; i++) {
+		ret = regmap_read(haptics->regmap,
+				  DA7280_GPI_0_CTL + i, &val);
+		if (ret) {
+			dev_err(haptics->dev,
+				"failed to read register : %d\n", ret);
+			return ret;
+		}
+		haptics->gpi_ctl[i].mode =
+			(val & DA7280_GPI0_MODE_MASK)
+				>> DA7280_GPI0_MODE_SHIFT;
+		val = 0;
+	}
+
+	return sprintf(buf, "Mode\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
+		haptics->gpi_ctl[0].mode,
+		haptics->gpi_ctl[1].mode,
+		haptics->gpi_ctl[2].mode);
+}
+
+/* Valid format for GPIx_MODE
+ *  echo X Y > gpi_pol
+ *  ex) echo 2 1 > /sys/class/..../gpi_pol
+ *  0 <= X < 3, 0<= Y <= 2.
+ */
+static ssize_t gpi_pol_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf,
+			     size_t count)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	u8 gpi_pol = 0xff, gpi_num = 0xff;
+	int ret;
+
+	if (count < 3)
+		goto err;
+
+	if (buf[0] >= '0')
+		gpi_num = buf[0] - '0';
+	if (buf[2] >= '0')
+		gpi_pol = buf[2] - '0';
+
+	if (gpi_num > 2 || gpi_pol > 2)
+		goto err;
+
+	ret = regmap_update_bits(haptics->regmap,
+				 DA7280_GPI_0_CTL + gpi_num,
+				 DA7280_GPI0_POLARITY_MASK,
+				 gpi_pol << DA7280_GPI0_POLARITY_SHIFT);
+	if (ret) {
+		dev_err(haptics->dev,
+			"failed to update register : %d\n", ret);
+		return ret;
+	}
+
+	haptics->gpi_ctl[gpi_num].polarity = gpi_pol;
+
+	return count;
+
+err:
+	dev_err(dev, "Invalid format or input values!\n");
+	return count;
+}
+
+static ssize_t gpi_pol_show(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+	unsigned int val, i;
+
+	for (i = 0; i < 3; i++) {
+		ret = regmap_read(haptics->regmap,
+				  DA7280_GPI_0_CTL + i, &val);
+		if (ret)
+			return ret;
+		haptics->gpi_ctl[i].polarity =
+			(val & DA7280_GPI0_POLARITY_MASK)
+			>> DA7280_GPI0_POLARITY_SHIFT;
+		val = 0;
+	}
+
+	return sprintf(buf, "Polarity\nGPI0 : %d\nGPI1 : %d\nGPI2 : %d\n",
+		haptics->gpi_ctl[0].polarity,
+		haptics->gpi_ctl[1].polarity,
+		haptics->gpi_ctl[2].polarity);
+}
+
+#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 ret;
+
+	ret = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
+	if (ret)
+		return ret;
+
+	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);
+
+	ret = da7280_haptic_mem_update(haptics);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(ps_seq_id);
+static DEVICE_ATTR_RW(ps_seq_loop);
+static DEVICE_ATTR_RW(gpi_seq_id);
+static DEVICE_ATTR_RW(gpi_mode);
+static DEVICE_ATTR_RW(gpi_pol);
+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_id.attr,
+	&dev_attr_gpi_mode.attr,
+	&dev_attr_gpi_pol.attr,
+	&dev_attr_patterns.attr,
+	NULL,
+};
+
+static const struct attribute_group da7280_attr_group = {
+	.attrs = da7280_sysfs_attr,
+};
+
+static const struct attribute_group *da7280_attr_groups[] = {
+	&da7280_attr_group,
+	NULL,
+};
+
+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 ret;
+
+	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;
+	}
+
+	/* Handle DT data if provided */
+	if (client->dev.of_node)
+		da7280_of_to_pdata(&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)) {
+		ret = PTR_ERR(haptics->regmap);
+		dev_err(dev, "Failed to allocate register map : %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					da7280_irq_handler,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"da7280-haptics", haptics);
+	if (ret != 0) {
+		dev_err(dev,
+			"Failed to request IRQ : %d\n", client->irq);
+		return ret;
+	}
+
+	ret = da7280_init(haptics);
+	if (ret) {
+		dev_err(dev, "failed to initialize device\n");
+		return ret;
+	}
+
+	/* 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);
+
+	ret = input_ff_create_memless(haptics->input_dev, NULL,
+				      da7280_haptic_play);
+	if (ret) {
+		dev_err(dev, "failed to create force-feedback\n");
+		return ret;
+	}
+
+#ifdef CONFIG_SYSFS
+	haptics->input_dev->dev.groups = da7280_attr_groups;
+#endif
+
+	ret = input_register_device(haptics->input_dev);
+	if (ret)
+		dev_err(dev, "failed to register input device\n");
+
+	return ret;
+}
+
+static int __maybe_unused da7280_suspend(struct device *dev)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+	if (haptics->suspend_state == 0) {
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CTL1,
+					 DA7280_STANDBY_EN_MASK, 0);
+		if (ret)
+			dev_err(haptics->dev,
+				"I2C error : %d\n", ret);
+		else
+			haptics->suspend_state = 1;
+	}
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static int __maybe_unused da7280_resume(struct device *dev)
+{
+	struct da7280_haptic *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+	if (haptics->suspend_state) {
+		ret = regmap_update_bits(haptics->regmap,
+					 DA7280_TOP_CTL1,
+					 DA7280_STANDBY_EN_MASK,
+					 DA7280_STANDBY_EN_MASK);
+		if (ret)
+			dev_err(haptics->dev,
+				"i2c error : %d\n", ret);
+		else
+			haptics->suspend_state = 0;
+	}
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+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 V6

^ permalink raw reply related

* [RESEND PATCH V6 2/3] dt-bindings: input: Add document bindings for DA7280
From: Roy Im @ 2018-09-19 10:35 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring
  Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
	linux-kernel
In-Reply-To: <cover.1537353304.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>

---
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 V6

^ permalink raw reply related

* [RESEND PATCH V6 0/3]  da7280: haptic driver submission
From: Roy Im @ 2018-09-19 10:35 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 V2 1/3] MAINTAINERS file update for DA7280
[PATCH V2 2/3] DA7280 DT Binding
[PATCH V2 3/3] DA7280 Driver

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

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                        | 1438 ++++++++++++++++++++
 drivers/input/misc/da7280.h                        |  412 ++++++
 6 files changed, 1971 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 V6

^ permalink raw reply

* Re: [RFC/PATCH 4/5] gpiolib: add support for fetching descriptors from static properties
From: Mika Westerberg @ 2018-09-19  8:33 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180918170418.GC177805@dtor-ws>

On Tue, Sep 18, 2018 at 10:04:18AM -0700, Dmitry Torokhov wrote:
> I am not sure how that would work, as there are multiple properties in
> that child array, so we can't simply take the first entry or assume that
> all entries describe GPIOs. Here is the fuller example:
> 
> static const struct property_entry simone_key_enter_props[] __initconst = {
> 	PROPERTY_ENTRY_U32("linux,code",	KEY_ENTER),
> 	PROPERTY_ENTRY_STRING("label",		"enter"),
> 	PROPERTY_ENTRY_STRING("gpios",		"enter-gpios"),
> 	{ }
> };
> 
> static const struct property_entry simone_key_up_props[] __initconst = {
> 	PROPERTY_ENTRY_U32("linux,code",	KEY_UP),
> 	PROPERTY_ENTRY_STRING("label",		"up"),
> 	PROPERTY_ENTRY_STRING("gpios",		"up-gpios"),
> 	{ }
> };
> 
> static const struct property_entry simone_key_up_props[] __initconst = {
> 	PROPERTY_ENTRY_U32("linux,code",	KEY_LEFT),
> 	PROPERTY_ENTRY_STRING("label",		"left"),
> 	PROPERTY_ENTRY_STRING("gpios",		"left-gpios"),
> 	{ }
> };
> 
> static const struct property_entry simone_key_props[] __initconst = {
> 	/* There are no properties at device level on this device */
> 	{ }
> };
> 
> static struct gpiod_lookup_table simone_keys_gpiod_table = {
> 	.dev_id = "gpio-keys",
> 	.table = {
> 		/* Use local offsets on gpiochip/port "B" */
> 		GPIO_LOOKUP_IDX("B", 0, "enter-gpios", 0, GPIO_ACTIVE_LOW),
> 		GPIO_LOOKUP_IDX("B", 1, "up-gpios", 1, GPIO_ACTIVE_LOW),
> 		GPIO_LOOKUP_IDX("B", 2, "left-gpios", 2, GPIO_ACTIVE_LOW),
> 	},
> };
> 
> static struct platform_device simone_keys_device = {
> 	.name = "gpio-keys",
> 	.id = -1,
> };
> 
> static void __init simone_init_machine(void)
> {
> 	...
> 	gpiod_add_lookup_table(&simone_keys_gpiod_table);
> 	device_add_properties(&simone_keys_device.dev,
> 			      simone_keys_device_props);
> 	device_add_child_properties(&simone_keys_device.dev,
> 				    dev_fwnode(&simone_keys_device.dev),
> 				    simone_key_enter_props);
> 	device_add_child_properties(&simone_keys_device.dev,
> 				    dev_fwnode(&simone_keys_device.dev),
> 				    simone_key_up_props);
> 	device_add_child_properties(&simone_keys_device.dev,
> 				    dev_fwnode(&simone_keys_device.dev),
> 				    simone_key_left_props);
> 	platform_device_register(&simone_keys_device);
> 	...
> }

Thanks for clarifying. I missed this last part where you feed the
properties to the device.

So looks fine by me :)

^ permalink raw reply

* Re: [PATCH 2/2] dt-bindings: egalax-ts: add support for wakeup event action
From: Rob Herring @ 2018-09-19  1:01 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Anson Huang, Mark Rutland, Marco Franchi, Fabio Estevam,
	linux-input, devicetree, linux-kernel@vger.kernel.org,
	NXP Linux Team
In-Reply-To: <20180917180744.GB104770@dtor-ws>

On Mon, Sep 17, 2018 at 11:07 AM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
>
> On Sun, Sep 16, 2018 at 05:54:33PM -0500, Rob Herring wrote:
> > On Wed, Sep 05, 2018 at 05:26:47PM +0800, Anson Huang wrote:
> > > Add support for wakeup event action, this would allow the device
> > > to configure whether to be a wakeup source of system suspend.
> > >
> > > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > > ---
> > >  Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt | 2 ++
> > >  1 file changed, 2 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt
> > > index 92fb262..296a9cd 100644
> > > --- a/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt
> > > +++ b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt
> > > @@ -6,6 +6,7 @@ Required properties:
> > >  - interrupts: touch controller interrupt
> > >  - wakeup-gpios: the gpio pin to be used for waking up the controller
> > >    and also used as irq pin
> >
> > If the wakeup gpio is the same as the irq, then this should be removed
> > since wakeup-source replaces it.
>
> No, it does not: it is GPIO that we need to toggle to wake up the
> controller, not host, and we do not have generic API to map from IRQ to
> GPIO.

Ah, okay.

> This is also existing binding...

Right, I meant removed as in moved to deprecated. Sometimes we just
remove the documentation when deprecating.

Rob

^ permalink raw reply

* Re: [RFC/PATCH 0/5] Support children for legacy device properties
From: Rafael J. Wysocki @ 2018-09-18 20:05 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, linux-input, linux-gpio, linux-kernel,
	Andy Shevchenko
In-Reply-To: <20180917181603.125492-1-dmitry.torokhov@gmail.com>

On 9/17/2018 8:15 PM, Dmitry Torokhov wrote:
> The generic device properties APIs are very helpful as they allow abstracting
> away details of the platform (whether it is ACPI, device tree, or legacy board
> file), so that individual driver does not need separate code paths to support
> all variants. However there are drivers that currently can not use generic
> device properties API as they need notion of children properties, for example
> gpio_keys driver, that expects every button to be described as a sub-node of
> main device.
>
> This patch series introduces notion of sub-nodes for static properties and ties
> it up with GPIO lookup tables so that they are usable with sub-nodes as well.

I don't have any objections against this series.

Thanks,
Rafael

^ permalink raw reply

* Re: [PATCH v2 05/17] compat_ioctl: move more drivers to generic_compat_ioctl_ptrarg
From: Jason Gunthorpe @ 2018-09-18 17:59 UTC (permalink / raw)
  To: Darren Hart
  Cc: Al Viro, Arnd Bergmann, linux-fsdevel, Greg Kroah-Hartman,
	David S. Miller, devel, linux-kernel, qat-linux, linux-crypto,
	linux-media, dri-devel, linaro-mm-sig, amd-gfx, linux-input,
	linux-iio, linux-rdma, linux-nvdimm, linux-nvme, linux-pci,
	platform-driver-x86, linux-remoteproc, sparclinux, linux-scsi,
	linux-usb, linux-fbdev, linuxppc-d
In-Reply-To: <20180918175108.GF35251@wrath>

On Tue, Sep 18, 2018 at 10:51:08AM -0700, Darren Hart wrote:
> On Fri, Sep 14, 2018 at 09:57:48PM +0100, Al Viro wrote:
> > On Fri, Sep 14, 2018 at 01:35:06PM -0700, Darren Hart wrote:
> >  
> > > Acked-by: Darren Hart (VMware) <dvhart@infradead.org>
> > > 
> > > As for a longer term solution, would it be possible to init fops in such
> > > a way that the compat_ioctl call defaults to generic_compat_ioctl_ptrarg
> > > so we don't have to duplicate this boilerplate for every ioctl fops
> > > structure?
> > 
> > 	Bad idea, that...  Because several years down the road somebody will add
> > an ioctl that takes an unsigned int for argument.  Without so much as looking
> > at your magical mystery macro being used to initialize file_operations.
> 
> Fair, being explicit in the declaration as it is currently may be
> preferable then.

It would be much cleaner and safer if you could arrange things to add
something like this to struct file_operations:

  long (*ptr_ioctl) (struct file *, unsigned int, void __user *);

Where the core code automatically converts the unsigned long to the
void __user * as appropriate.

Then it just works right always and the compiler will help address
Al's concern down the road.

Cheers,
Jason

^ permalink raw reply

* Re: [PATCH v2 05/17] compat_ioctl: move more drivers to generic_compat_ioctl_ptrarg
From: Darren Hart @ 2018-09-18 17:51 UTC (permalink / raw)
  To: Al Viro
  Cc: Arnd Bergmann, linux-fsdevel, Greg Kroah-Hartman, David S. Miller,
	devel, linux-kernel, qat-linux, linux-crypto, linux-media,
	dri-devel, linaro-mm-sig, amd-gfx, linux-input, linux-iio,
	linux-rdma, linux-nvdimm, linux-nvme, linux-pci,
	platform-driver-x86, linux-remoteproc, sparclinux, linux-scsi,
	linux-usb, linux-fbdev, linuxppc-dev, linux-btrfs
In-Reply-To: <20180914205748.GC19965@ZenIV.linux.org.uk>

On Fri, Sep 14, 2018 at 09:57:48PM +0100, Al Viro wrote:
> On Fri, Sep 14, 2018 at 01:35:06PM -0700, Darren Hart wrote:
>  
> > Acked-by: Darren Hart (VMware) <dvhart@infradead.org>
> > 
> > As for a longer term solution, would it be possible to init fops in such
> > a way that the compat_ioctl call defaults to generic_compat_ioctl_ptrarg
> > so we don't have to duplicate this boilerplate for every ioctl fops
> > structure?
> 
> 	Bad idea, that...  Because several years down the road somebody will add
> an ioctl that takes an unsigned int for argument.  Without so much as looking
> at your magical mystery macro being used to initialize file_operations.

Fair, being explicit in the declaration as it is currently may be
preferable then.

-- 
Darren Hart
VMware Open Source Technology Center

^ permalink raw reply

* Re: [PATCH 2/2] input: atakbd.c - fix Atari CapsLock behaviour
From: Dmitry Torokhov @ 2018-09-18 17:08 UTC (permalink / raw)
  To: Michael Schmitz; +Cc: Andreas Schwab, linux-input, debian-68k, linux-m68k
In-Reply-To: <136f4a00-ba74-a705-ef31-29c60eb51aa1@gmail.com>

Hi Michael,

On Tue, Sep 18, 2018 at 02:03:29PM +1200, Michael Schmitz wrote:
> Thanks Dmitry!
> 
> I had asked Andreas' signoff for his keymap fixes only - the rewrite of the
> Atari keyboard driver to the current input framework (and hence the CapsLock
> toggle bug) was entirely mine, no blame attaches to Andreas there.

It was not about blame, it was about helping me to check the sanity of
the patch given I have no idea about the hardware ;)

Thanks.

^ permalink raw reply

* Re: [PATCH] Input: uinput - allow for max == min during input_absinfo validation
From: Dmitry Torokhov @ 2018-09-18 17:05 UTC (permalink / raw)
  To: Martin Kepplinger
  Cc: Peter Hutterer, linux-input, linux-kernel, Benjamin Tissoires
In-Reply-To: <19cbcf47-4ab4-7dd0-392a-9a47b7264999@ginzinger.com>

On Tue, Sep 18, 2018 at 01:13:47PM +0200, Martin Kepplinger wrote:
> On 9/18/18 7:22 AM, Peter Hutterer wrote:
> > These values are inclusive, so a range of 1 requires min == max.
> > 
> > Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> 
> true, nice catch.
> 
> Reviewed-by: Martin Kepplinger <martin.kepplinger@ginzinger.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply

* Re: [RFC/PATCH 4/5] gpiolib: add support for fetching descriptors from static properties
From: Dmitry Torokhov @ 2018-09-18 17:04 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Linus Walleij, Rafael J . Wysocki, linux-input, linux-gpio,
	linux-kernel, Andy Shevchenko
In-Reply-To: <20180918090219.GE14465@lahna.fi.intel.com>

Hi Mika,

On Tue, Sep 18, 2018 at 12:02:19PM +0300, Mika Westerberg wrote:
> Hi,
> 
> On Mon, Sep 17, 2018 at 11:16:02AM -0700, Dmitry Torokhov wrote:
> > Now that static device properties understand notion of child nodes, let's
> > teach gpiolib to tie such children and machine GPIO descriptor tables.
> > We will continue using a single table for entire device, but instead of
> > using connection ID as a lookup key in the GPIO descriptor table directly,
> > we will perform additional translation: fwnode_get_named_gpiod() when
> > dealing with property_set-backed fwnodes will try parsing string property
> > with name matching connection ID and use result of the lookup as the key in
> > the table:
> > 
> > static const struct property_entry dev_child1_props[] __initconst = {
> > 	...
> > 	PROPERTY_ENTRY_STRING("gpios",		"child-1-gpios"),
> > 	{ }
> > };
> > 
> > static struct gpiod_lookup_table dev_gpiod_table = {
> > 	.dev_id = "some-device",
> > 	.table = {
> > 		...
> > 		GPIO_LOOKUP_IDX("B", 1, "child-1-gpios", 1, GPIO_ACTIVE_LOW),
> > 		...
> > 	},
> > };
> 
> I wonder if instead of passing and parsing strings (and hoping there are
> no typos) we could get the compiler to help us bit more?
> 
> Something like below:
> 
> static const struct property_entry dev_child1_props[] __initconst = {
>  	...
>  	PROPERTY_ENTRY_STRING("gpios","child-1-gpios"),
>  	{ }
> };
>  
> static struct gpiod_lookup_table dev_gpiod_table = {
>  	.dev_id = "some-device",
>  	.table = {
>  		...
>  		GPIO_LOOKUP_IDX("B", 1, dev_child1_props, SIZEOF(dev_child1_props),
> 				1, GPIO_ACTIVE_LOW),
>  		...
>  	},
> };

I am not sure how that would work, as there are multiple properties in
that child array, so we can't simply take the first entry or assume that
all entries describe GPIOs. Here is the fuller example:

static const struct property_entry simone_key_enter_props[] __initconst = {
	PROPERTY_ENTRY_U32("linux,code",	KEY_ENTER),
	PROPERTY_ENTRY_STRING("label",		"enter"),
	PROPERTY_ENTRY_STRING("gpios",		"enter-gpios"),
	{ }
};

static const struct property_entry simone_key_up_props[] __initconst = {
	PROPERTY_ENTRY_U32("linux,code",	KEY_UP),
	PROPERTY_ENTRY_STRING("label",		"up"),
	PROPERTY_ENTRY_STRING("gpios",		"up-gpios"),
	{ }
};

static const struct property_entry simone_key_up_props[] __initconst = {
	PROPERTY_ENTRY_U32("linux,code",	KEY_LEFT),
	PROPERTY_ENTRY_STRING("label",		"left"),
	PROPERTY_ENTRY_STRING("gpios",		"left-gpios"),
	{ }
};

static const struct property_entry simone_key_props[] __initconst = {
	/* There are no properties at device level on this device */
	{ }
};

static struct gpiod_lookup_table simone_keys_gpiod_table = {
	.dev_id = "gpio-keys",
	.table = {
		/* Use local offsets on gpiochip/port "B" */
		GPIO_LOOKUP_IDX("B", 0, "enter-gpios", 0, GPIO_ACTIVE_LOW),
		GPIO_LOOKUP_IDX("B", 1, "up-gpios", 1, GPIO_ACTIVE_LOW),
		GPIO_LOOKUP_IDX("B", 2, "left-gpios", 2, GPIO_ACTIVE_LOW),
	},
};

static struct platform_device simone_keys_device = {
	.name = "gpio-keys",
	.id = -1,
};

static void __init simone_init_machine(void)
{
	...
	gpiod_add_lookup_table(&simone_keys_gpiod_table);
	device_add_properties(&simone_keys_device.dev,
			      simone_keys_device_props);
	device_add_child_properties(&simone_keys_device.dev,
				    dev_fwnode(&simone_keys_device.dev),
				    simone_key_enter_props);
	device_add_child_properties(&simone_keys_device.dev,
				    dev_fwnode(&simone_keys_device.dev),
				    simone_key_up_props);
	device_add_child_properties(&simone_keys_device.dev,
				    dev_fwnode(&simone_keys_device.dev),
				    simone_key_left_props);
	platform_device_register(&simone_keys_device);
	...
}

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: elantech - enable middle button of touchpad on ThinkPad P72
From: Dmitry Torokhov @ 2018-09-18 16:32 UTC (permalink / raw)
  To: Aaron Ma; +Cc: linux-input, linux-kernel
In-Reply-To: <20180907070651.6916-1-aaron.ma@canonical.com>

On Fri, Sep 07, 2018 at 03:06:51PM +0800, Aaron Ma wrote:
> Adding 2 new touchpad IDs to support middle button support.
> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Aaron Ma <aaron.ma@canonical.com>

Applied, thank you.

> ---
>  drivers/input/mouse/elantech.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
> index 44f57cf6675b..2d95e8d93cc7 100644
> --- a/drivers/input/mouse/elantech.c
> +++ b/drivers/input/mouse/elantech.c
> @@ -1178,6 +1178,8 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
>  static const char * const middle_button_pnp_ids[] = {
>  	"LEN2131", /* ThinkPad P52 w/ NFC */
>  	"LEN2132", /* ThinkPad P52 */
> +	"LEN2133", /* ThinkPad P72 w/ NFC */
> +	"LEN2134", /* ThinkPad P72 */
>  	NULL
>  };
>  
> -- 
> 2.17.1
> 

-- 
Dmitry

^ permalink raw reply


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