* Re: [PATCH] Input: elants_i2c - Fix sw reset delays
From: Andi Shyti @ 2018-08-24 8:49 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Derek Basehore, Johnny Chuang, linux-kernel, linux-input, joe,
andi, gregkh, James Chen, KT Liao
In-Reply-To: <20180823233424.GE53155@dtor-ws>
Hi Derek,
> > > On Thu, Aug 23, 2018 at 04:10:13PM -0700, Derek Basehore wrote:
> > > > We only need to wait 10ms instead of 30ms before starting fastboot or
> > > > sending IAP on the touchscreen. Also, instead of delaying everytime
> > > > sw_reset is called, this delays 10ms in the function that starts
> > > > fastboot. There's also an explicit 20ms delay before sending IAP when
> > > > updating the firmware, so no additional delay is needed there. This
> > > > change also has the benefit of not delaying when wakeup is enabled
> > > > during suspend. This is because sw_reset is called, yet fastboot
> > > > isn't.
...
> > > > - /*
> > > > - * We should wait at least 10 msec (but no more than 40) before
> > > > - * sending fastboot or IAP command to the device.
> > > > - */
> > > > - msleep(30);
> > > > -
moving from 30 to 0 is a bit alarming... what does the datasheet
say?
Sometimes delays are implicit in the system where you are testing
the driver, so that without any msleep it might work in your
system but it might not on others.
> + /*
> + * We should wait at least 10 msec (but no more than 40) before
> + * sending IAP command to the device.
> + */
> msleep(20);
I agree though that it's not nice to wait twice here (even though
as Dmitry says it doesn't hurt so much). Wouldn't it make more
sense to remove this msleep instead?
This way...
> + /*
> + * We should wait at least 10 msec (but no more than 40) before sending
> + * fastboot command to the device.
> + */
> + usleep_range(10 * 1000, 11 * 1000);
> +
> error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
... you do not need to add an extra sleep here.
Andi
^ permalink raw reply
* RE: [PATCH V4 3/3] Input: new da7280 haptic driver
From: Roy Im @ 2018-08-24 5:35 UTC (permalink / raw)
To: Randy Dunlap, Roy Im, Dmitry Torokhov, Rob Herring
Cc: Mark Rutland, Support Opensource, devicetree@vger.kernel.org,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <6115d6ca-8df6-1830-3c61-681610d8f2b0@infradead.org>
Hi Randy,
On 08/24/2018 12:28 AM, Randy Dunlap wrote:
> On 08/20/2018 10:44 PM, Roy Im wrote:
> > diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> > index ca59a2b..6e0de69 100644
> > --- a/drivers/input/misc/Kconfig
> > +++ b/drivers/input/misc/Kconfig
> > @@ -851,4 +851,16 @@ config INPUT_SC27XX_VIBRA
> > To compile this driver as a module, choose M here. The module
> will
> > be called sc27xx_vibra.
> >
>
> Hi,
> Can you explain the "depends on" below, please.
>
> > +config INPUT_DA7280_HAPTICS
> > + tristate "Dialog Semiconductor DA7280 haptics support"
> > + depends on (INPUT && I2C && SYSFS) || PWM
> > + select INPUT_FF_MEMLESS
> > + select REGMAP_I2C
>
> If INPUT is disabled, and I2C is disabled, and SYSFS is disabled, but
> PWM=y,
> then the "select"s will have problems with unmet dependencies.
I will fix this in v5.
>
> > + help
> > + Say Y to enable support for the haptics controller on
> > + Dialog DA7280 chip.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called da7280-haptic.
>
> That module name doesn't match what's in the Makefile (below).
I will fix this as well in v5.
>
> > +
> > 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
>
>
> --
> ~Randy
Regards,
Roy
^ permalink raw reply
* [PATCH v2] Input: synaptics - Lenovo ThinkPad T25 and T480 devices should use RMI
From: Teika Kazura @ 2018-08-24 5:07 UTC (permalink / raw)
To: dmitry.torokhov, linux-input, linux-kernel; +Cc: kitsunyan, benjamin.tissoires
In-Reply-To: <20180707051913.24035-1-kitsunyan@inbox.ru>
The touchpads on both T25 and T480 are accessible over SMBUS/RMI.
---
v2
Only a tag change.
The original author wants to use a pseudonym, and agrees with the usurpation of the signed-off-by tag. See https://patchwork.kernel.org/patch/10512751/
Reported-by: kitsunyan <kitsunyan@inbox.ru>
Signed-off-by: Teika Kazura <teika@gmx.com>
---
drivers/input/mouse/synaptics.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 55d33500d55e..be934a082424 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -175,7 +175,9 @@ static const char * const smbus_pnp_ids[] = {
"LEN0071", /* T480 */
"LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
"LEN0073", /* X1 Carbon G5 (Elantech) */
+ "LEN008e", /* T25 */
"LEN0092", /* X1 Carbon 6 */
+ "LEN0093", /* T480 */
"LEN0096", /* X280 */
"LEN0097", /* X280 -> ALPS trackpoint */
"LEN200f", /* T450s */
--
2.18.0
^ permalink raw reply related
* Re: [PATCH] Input: elants_i2c - Fix sw reset delays
From: Dmitry Torokhov @ 2018-08-23 23:34 UTC (permalink / raw)
To: Derek Basehore, Johnny Chuang
Cc: linux-kernel, linux-input, joe, andi, gregkh, James Chen, KT Liao
In-Reply-To: <20180823233211.GD53155@dtor-ws>
For real now...
On Thu, Aug 23, 2018 at 04:32:11PM -0700, Dmitry Torokhov wrote:
> Let's add Elan folks to the discussion.
>
> On Thu, Aug 23, 2018 at 04:30:28PM -0700, Dmitry Torokhov wrote:
> > Hi Derek,
> >
> > On Thu, Aug 23, 2018 at 04:10:13PM -0700, Derek Basehore wrote:
> > > We only need to wait 10ms instead of 30ms before starting fastboot or
> > > sending IAP on the touchscreen. Also, instead of delaying everytime
> > > sw_reset is called, this delays 10ms in the function that starts
> > > fastboot. There's also an explicit 20ms delay before sending IAP when
> > > updating the firmware, so no additional delay is needed there. This
> > > change also has the benefit of not delaying when wakeup is enabled
> > > during suspend. This is because sw_reset is called, yet fastboot
> > > isn't.
> > >
> > > Change-Id: I9e3019720186ba0023891fafeb4fe3d2510e454b
> >
> > This is not needed ;)
> >
> > > Signed-off-by: Derek Basehore <dbasehore@chromium.org>
> > > ---
> > > drivers/input/touchscreen/elants_i2c.c | 16 ++++++++++------
> > > 1 file changed, 10 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
> > > index d21ca39b0fdb..18ce04ba0173 100644
> > > --- a/drivers/input/touchscreen/elants_i2c.c
> > > +++ b/drivers/input/touchscreen/elants_i2c.c
> > > @@ -284,12 +284,6 @@ static int elants_i2c_sw_reset(struct i2c_client *client)
> > > return error;
> > > }
> > >
> > > - /*
> > > - * We should wait at least 10 msec (but no more than 40) before
> > > - * sending fastboot or IAP command to the device.
> > > - */
> > > - msleep(30);
> > > -
> > > return 0;
> > > }
> > >
> > > @@ -500,6 +494,12 @@ static int elants_i2c_fastboot(struct i2c_client *client)
> > > const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
> > > int error;
> > >
> > > + /*
> > > + * We should wait at least 10 msec (but no more than 40) before sending
> > > + * fastboot command to the device.
> > > + */
> > > + usleep_range(10 * 1000, 11 * 1000);
> > > +
> > > error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
> > > if (error) {
> > > dev_err(&client->dev, "boot failed: %d\n", error);
> > > @@ -643,6 +643,10 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
> > > dev_err(&client->dev, "Failed close idle: %d\n", error);
> > > msleep(60);
> > > elants_i2c_sw_reset(client);
> > > + /*
> > > + * We should wait at least 10 msec (but no more than 40) before
> > > + * sending IAP command to the device.
> > > + */
> > > msleep(20);
> >
> > So the original comment was talking about timing on fastboot or IAP
> > command, but the original code had 50 msec wait here (30 from
> > elants_i2c_sw_reset plus the 20), thus already violating 40 msec rule
> > for IAP. Unless Elan folks can confirm it is OK to reduce the wait here
> > I'd prefer we kept it at 50. Firmware update is does not happen that
> > often anyway.
> >
> > Thanks.
> >
> > --
> > Dmitry
>
> --
> Dmitry
--
Dmitry
^ permalink raw reply
* Re: [PATCH] Input: elants_i2c - Fix sw reset delays
From: Dmitry Torokhov @ 2018-08-23 23:32 UTC (permalink / raw)
To: Derek Basehore; +Cc: linux-kernel, linux-input, joe, andi, gregkh
In-Reply-To: <20180823233028.GC53155@dtor-ws>
Let's add Elan folks to the discussion.
On Thu, Aug 23, 2018 at 04:30:28PM -0700, Dmitry Torokhov wrote:
> Hi Derek,
>
> On Thu, Aug 23, 2018 at 04:10:13PM -0700, Derek Basehore wrote:
> > We only need to wait 10ms instead of 30ms before starting fastboot or
> > sending IAP on the touchscreen. Also, instead of delaying everytime
> > sw_reset is called, this delays 10ms in the function that starts
> > fastboot. There's also an explicit 20ms delay before sending IAP when
> > updating the firmware, so no additional delay is needed there. This
> > change also has the benefit of not delaying when wakeup is enabled
> > during suspend. This is because sw_reset is called, yet fastboot
> > isn't.
> >
> > Change-Id: I9e3019720186ba0023891fafeb4fe3d2510e454b
>
> This is not needed ;)
>
> > Signed-off-by: Derek Basehore <dbasehore@chromium.org>
> > ---
> > drivers/input/touchscreen/elants_i2c.c | 16 ++++++++++------
> > 1 file changed, 10 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
> > index d21ca39b0fdb..18ce04ba0173 100644
> > --- a/drivers/input/touchscreen/elants_i2c.c
> > +++ b/drivers/input/touchscreen/elants_i2c.c
> > @@ -284,12 +284,6 @@ static int elants_i2c_sw_reset(struct i2c_client *client)
> > return error;
> > }
> >
> > - /*
> > - * We should wait at least 10 msec (but no more than 40) before
> > - * sending fastboot or IAP command to the device.
> > - */
> > - msleep(30);
> > -
> > return 0;
> > }
> >
> > @@ -500,6 +494,12 @@ static int elants_i2c_fastboot(struct i2c_client *client)
> > const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
> > int error;
> >
> > + /*
> > + * We should wait at least 10 msec (but no more than 40) before sending
> > + * fastboot command to the device.
> > + */
> > + usleep_range(10 * 1000, 11 * 1000);
> > +
> > error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
> > if (error) {
> > dev_err(&client->dev, "boot failed: %d\n", error);
> > @@ -643,6 +643,10 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
> > dev_err(&client->dev, "Failed close idle: %d\n", error);
> > msleep(60);
> > elants_i2c_sw_reset(client);
> > + /*
> > + * We should wait at least 10 msec (but no more than 40) before
> > + * sending IAP command to the device.
> > + */
> > msleep(20);
>
> So the original comment was talking about timing on fastboot or IAP
> command, but the original code had 50 msec wait here (30 from
> elants_i2c_sw_reset plus the 20), thus already violating 40 msec rule
> for IAP. Unless Elan folks can confirm it is OK to reduce the wait here
> I'd prefer we kept it at 50. Firmware update is does not happen that
> often anyway.
>
> Thanks.
>
> --
> Dmitry
--
Dmitry
^ permalink raw reply
* Re: [PATCH] Input: elants_i2c - Fix sw reset delays
From: Dmitry Torokhov @ 2018-08-23 23:30 UTC (permalink / raw)
To: Derek Basehore; +Cc: linux-kernel, linux-input, joe, andi, gregkh
In-Reply-To: <20180823231013.254037-1-dbasehore@chromium.org>
Hi Derek,
On Thu, Aug 23, 2018 at 04:10:13PM -0700, Derek Basehore wrote:
> We only need to wait 10ms instead of 30ms before starting fastboot or
> sending IAP on the touchscreen. Also, instead of delaying everytime
> sw_reset is called, this delays 10ms in the function that starts
> fastboot. There's also an explicit 20ms delay before sending IAP when
> updating the firmware, so no additional delay is needed there. This
> change also has the benefit of not delaying when wakeup is enabled
> during suspend. This is because sw_reset is called, yet fastboot
> isn't.
>
> Change-Id: I9e3019720186ba0023891fafeb4fe3d2510e454b
This is not needed ;)
> Signed-off-by: Derek Basehore <dbasehore@chromium.org>
> ---
> drivers/input/touchscreen/elants_i2c.c | 16 ++++++++++------
> 1 file changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
> index d21ca39b0fdb..18ce04ba0173 100644
> --- a/drivers/input/touchscreen/elants_i2c.c
> +++ b/drivers/input/touchscreen/elants_i2c.c
> @@ -284,12 +284,6 @@ static int elants_i2c_sw_reset(struct i2c_client *client)
> return error;
> }
>
> - /*
> - * We should wait at least 10 msec (but no more than 40) before
> - * sending fastboot or IAP command to the device.
> - */
> - msleep(30);
> -
> return 0;
> }
>
> @@ -500,6 +494,12 @@ static int elants_i2c_fastboot(struct i2c_client *client)
> const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
> int error;
>
> + /*
> + * We should wait at least 10 msec (but no more than 40) before sending
> + * fastboot command to the device.
> + */
> + usleep_range(10 * 1000, 11 * 1000);
> +
> error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
> if (error) {
> dev_err(&client->dev, "boot failed: %d\n", error);
> @@ -643,6 +643,10 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
> dev_err(&client->dev, "Failed close idle: %d\n", error);
> msleep(60);
> elants_i2c_sw_reset(client);
> + /*
> + * We should wait at least 10 msec (but no more than 40) before
> + * sending IAP command to the device.
> + */
> msleep(20);
So the original comment was talking about timing on fastboot or IAP
command, but the original code had 50 msec wait here (30 from
elants_i2c_sw_reset plus the 20), thus already violating 40 msec rule
for IAP. Unless Elan folks can confirm it is OK to reduce the wait here
I'd prefer we kept it at 50. Firmware update is does not happen that
often anyway.
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH] Input: elants_i2c - Fix sw reset delays
From: Derek Basehore @ 2018-08-23 23:10 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, joe, andi, gregkh, dmitry.torokhov, Derek Basehore
We only need to wait 10ms instead of 30ms before starting fastboot or
sending IAP on the touchscreen. Also, instead of delaying everytime
sw_reset is called, this delays 10ms in the function that starts
fastboot. There's also an explicit 20ms delay before sending IAP when
updating the firmware, so no additional delay is needed there. This
change also has the benefit of not delaying when wakeup is enabled
during suspend. This is because sw_reset is called, yet fastboot
isn't.
Change-Id: I9e3019720186ba0023891fafeb4fe3d2510e454b
Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
drivers/input/touchscreen/elants_i2c.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index d21ca39b0fdb..18ce04ba0173 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -284,12 +284,6 @@ static int elants_i2c_sw_reset(struct i2c_client *client)
return error;
}
- /*
- * We should wait at least 10 msec (but no more than 40) before
- * sending fastboot or IAP command to the device.
- */
- msleep(30);
-
return 0;
}
@@ -500,6 +494,12 @@ static int elants_i2c_fastboot(struct i2c_client *client)
const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
int error;
+ /*
+ * We should wait at least 10 msec (but no more than 40) before sending
+ * fastboot command to the device.
+ */
+ usleep_range(10 * 1000, 11 * 1000);
+
error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
if (error) {
dev_err(&client->dev, "boot failed: %d\n", error);
@@ -643,6 +643,10 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
dev_err(&client->dev, "Failed close idle: %d\n", error);
msleep(60);
elants_i2c_sw_reset(client);
+ /*
+ * We should wait at least 10 msec (but no more than 40) before
+ * sending IAP command to the device.
+ */
msleep(20);
error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
}
--
2.19.0.rc0.228.g281dcd1b4d0-goog
^ permalink raw reply related
* Re: [PATCH 1/3] Add the `REL_WHEEL_HI_RES` event code
From: Dmitry Torokhov @ 2018-08-23 19:01 UTC (permalink / raw)
To: Matthew Wilcox
Cc: Harry Cutts, linux-input, LKML, Jiri Kosina, Benjamin Tissoires,
linux-doc, Jonathan Corbet
In-Reply-To: <20180823185722.GA24777@bombadil.infradead.org>
On Thu, Aug 23, 2018 at 11:57:22AM -0700, Matthew Wilcox wrote:
> On Thu, Aug 23, 2018 at 11:30:55AM -0700, Harry Cutts wrote:
> > + - If a vertical scroll wheel supports high-resolution scrolling, this code
> > + will be emitted in addition to REL_WHEEL. The value is the (approximate)
> > + distance travelled by the user's finger, in 256ths of a millimeter.
>
> Is it too late to change this to an actual unit like micrometres?
No love for imperial roots? Sure, micrometers would work too I think.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 1/3] Add the `REL_WHEEL_HI_RES` event code
From: Matthew Wilcox @ 2018-08-23 18:57 UTC (permalink / raw)
To: Harry Cutts
Cc: linux-input, LKML, Jiri Kosina, Dmitry Torokhov,
Benjamin Tissoires, linux-doc, Jonathan Corbet
In-Reply-To: <20180823183057.247630-2-hcutts@chromium.org>
On Thu, Aug 23, 2018 at 11:30:55AM -0700, Harry Cutts wrote:
> + - If a vertical scroll wheel supports high-resolution scrolling, this code
> + will be emitted in addition to REL_WHEEL. The value is the (approximate)
> + distance travelled by the user's finger, in 256ths of a millimeter.
Is it too late to change this to an actual unit like micrometres?
^ permalink raw reply
* Re: [PATCH 1/3] Add the `REL_WHEEL_HI_RES` event code
From: Dmitry Torokhov @ 2018-08-23 18:42 UTC (permalink / raw)
To: Harry Cutts
Cc: linux-input, LKML, Jiri Kosina, Benjamin Tissoires, linux-doc,
Jonathan Corbet
In-Reply-To: <20180823183057.247630-2-hcutts@chromium.org>
On Thu, Aug 23, 2018 at 11:30:55AM -0700, Harry Cutts wrote:
> This event code represents scroll reports from high-resolution wheels,
> and will be used by future patches in this series. See the linux-input
> "Reporting high-resolution scroll events" thread [0] for more details.
>
> [0]: https://www.spinics.net/lists/linux-input/msg57380.html
>
> Signed-off-by: Harry Cutts <hcutts@chromium.org>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Jiri, Benjamin, feel free to merge through HID tree if the rest is
acceptable.
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH 3/3] Enable high-resolution scrolling on Logitech mice
From: Harry Cutts @ 2018-08-23 18:30 UTC (permalink / raw)
To: linux-input, LKML
Cc: Jiri Kosina, Dmitry Torokhov, Benjamin Tissoires, Harry Cutts,
Jiri Kosina
In-Reply-To: <20180823183057.247630-1-hcutts@chromium.org>
There are three features used by various Logitech mice for
high-resolution scrolling: the fast scrolling bit in HID++ 1.0, and the
x2120 and x2121 features in HID++ 2.0 and above. This patch supports
all three, and uses the multiplier reported by the mouse for the HID++
2.0+ features.
The full list of product IDs of mice which support high-resolution
scrolling was provided by Logitech, but the patch was tested using the
following mice (over both Bluetooth and Unifying where applicable):
* HID++ 1.0: Anywhere MX, Performance MX
* x2120: M560
* x2121: MX Anywhere 2, MX Master 2S
Signed-off-by: Harry Cutts <hcutts@chromium.org>
---
drivers/hid/hid-ids.h | 15 ++
drivers/hid/hid-logitech-hidpp.c | 341 ++++++++++++++++++++++++++++---
drivers/hid/hid-quirks.c | 11 +
3 files changed, 340 insertions(+), 27 deletions(-)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 79bdf0c7e351..64fbe6174189 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -717,6 +717,21 @@
#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
+/*
+ * The following mice have different IDs over Bluetooth than Logitech Unifying
+ * protocol, hence the _BT suffix.
+ */
+#define USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT1 0xb014
+#define USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT2 0xb016
+#define USB_DEVICE_ID_LOGITECH_MOUSE_M720_BT 0xb015
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT1 0xb013
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT2 0xb018
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT3 0xb01f
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2S_BT 0xb01a
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT1 0xb012
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT2 0xb017
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT3 0xb01e
+#define USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_2S_BT 0xb019
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 19cc980eebce..17598b87f1b7 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -64,6 +64,14 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25)
+#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26)
+#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27)
+#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28)
+
+/* Convenience constant to check for any high-res support. */
+#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
+ HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
+ HIDPP_QUIRK_HI_RES_SCROLL_X2121)
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
@@ -149,6 +157,7 @@ struct hidpp_device {
unsigned long capabilities;
struct hidpp_battery battery;
+ struct hid_scroll_counter vertical_wheel_counter;
};
/* HID++ 1.0 error codes */
@@ -400,32 +409,53 @@ static void hidpp_prefix_name(char **name, int name_length)
#define HIDPP_SET_LONG_REGISTER 0x82
#define HIDPP_GET_LONG_REGISTER 0x83
-#define HIDPP_REG_GENERAL 0x00
-
-static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+/**
+ * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
+ * @hidpp_dev: the device to set the register on.
+ * @register_address: the address of the register to modify.
+ * @byte: the byte of the register to modify. Should be less than 3.
+ * Return: 0 if successful, otherwise a negative error code.
+ */
+static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
+ u8 register_address, u8 byte, u8 bit)
{
struct hidpp_report response;
int ret;
u8 params[3] = { 0 };
ret = hidpp_send_rap_command_sync(hidpp_dev,
- REPORT_ID_HIDPP_SHORT,
- HIDPP_GET_REGISTER,
- HIDPP_REG_GENERAL,
- NULL, 0, &response);
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_REGISTER,
+ register_address,
+ NULL, 0, &response);
if (ret)
return ret;
memcpy(params, response.rap.params, 3);
- /* Set the battery bit */
- params[0] |= BIT(4);
+ params[byte] |= BIT(bit);
return hidpp_send_rap_command_sync(hidpp_dev,
- REPORT_ID_HIDPP_SHORT,
- HIDPP_SET_REGISTER,
- HIDPP_REG_GENERAL,
- params, 3, &response);
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_SET_REGISTER,
+ register_address,
+ params, 3, &response);
+}
+
+
+#define HIDPP_REG_GENERAL 0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+{
+ return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
+}
+
+#define HIDPP_REG_FEATURES 0x01
+
+/* On HID++ 1.0 devices, high-res scrolling was called "fast scrolling". */
+static int hidpp10_enable_fast_scrolling(struct hidpp_device *hidpp_dev)
+{
+ return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
}
#define HIDPP_REG_BATTERY_STATUS 0x07
@@ -1136,6 +1166,101 @@ static int hidpp_battery_get_property(struct power_supply *psy,
return ret;
}
+/* -------------------------------------------------------------------------- */
+/* 0x2120: Hi-resolution scrolling */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120
+
+#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
+
+static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
+ bool enabled, u8 *multiplier)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ u8 params[1];
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp,
+ HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
+ &feature_index,
+ &feature_type);
+ if (ret)
+ return ret;
+
+ params[0] = enabled ? BIT(0) : 0;
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
+ params, sizeof(params), &response);
+ if (ret)
+ return ret;
+ *multiplier = response.fap.params[1];
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x2121: HiRes Wheel */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HIRES_WHEEL 0x2121
+
+#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00
+#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20
+
+static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
+ u8 *multiplier)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+ &feature_index, &feature_type);
+ if (ret)
+ goto return_default;
+
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
+ NULL, 0, &response);
+ if (ret)
+ goto return_default;
+
+ *multiplier = response.fap.params[0];
+ return 0;
+return_default:
+ *multiplier = 8;
+ hid_warn(hidpp->hid_dev,
+ "Couldn't get wheel multiplier (error %d), assuming %d.\n",
+ ret, *multiplier);
+ return ret;
+}
+
+static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
+ bool high_resolution, bool use_hidpp)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ u8 params[1];
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+ &feature_index, &feature_type);
+ if (ret)
+ return ret;
+
+ params[0] = (invert ? BIT(2) : 0) |
+ (high_resolution ? BIT(1) : 0) |
+ (use_hidpp ? BIT(0) : 0);
+
+ return hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HIRES_WHEEL_SET_WHEEL_MODE,
+ params, sizeof(params), &response);
+}
+
/* -------------------------------------------------------------------------- */
/* 0x4301: Solar Keyboard */
/* -------------------------------------------------------------------------- */
@@ -2399,7 +2524,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
input_report_rel(mydata->input, REL_Y, v);
v = hid_snto32(data[6], 8);
- input_report_rel(mydata->input, REL_WHEEL, v);
+ hid_scroll_counter_handle_scroll(
+ &hidpp->vertical_wheel_counter, v);
input_sync(mydata->input);
}
@@ -2527,6 +2653,71 @@ static int g920_get_config(struct hidpp_device *hidpp)
return 0;
}
+/* -------------------------------------------------------------------------- */
+/* High-resolution scroll wheels */
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel.
+ * @product_id: the HID product ID of the device being described.
+ * @mm256_per_hi_res_unit: the distance moved by the user's finger for each
+ * high-resolution unit reported by the device, in
+ * 256ths of a millimetre.
+ */
+struct hi_res_scroll_info {
+ __u32 product_id;
+ int mm256_per_hi_res_unit;
+};
+
+static struct hi_res_scroll_info hi_res_scroll_devices[] = {
+ { /* Anywhere MX */
+ .product_id = 0x1017, .mm256_per_hi_res_unit = 114 },
+ { /* Performance MX */
+ .product_id = 0x101a, .mm256_per_hi_res_unit = 104 },
+ { /* M560 */
+ .product_id = 0x402d, .mm256_per_hi_res_unit = 111 },
+ { /* MX Master 2S */
+ .product_id = 0x4069, .mm256_per_hi_res_unit = 104 },
+ { .product_id = USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_2S_BT,
+ .mm256_per_hi_res_unit = 104 },
+};
+
+static int hi_res_scroll_look_up_mm256(__u32 product_id)
+{
+ int i;
+ int num_devices = sizeof(hi_res_scroll_devices)
+ / sizeof(hi_res_scroll_devices[0]);
+ for (i = 0; i < num_devices; i++) {
+ if (hi_res_scroll_devices[i].product_id == product_id)
+ return hi_res_scroll_devices[i].mm256_per_hi_res_unit;
+ }
+ return 104;
+}
+
+static int hi_res_scroll_enable(struct hidpp_device *hidpp)
+{
+ int ret;
+ u8 multiplier;
+
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
+ ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
+ hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
+ } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
+ ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
+ &multiplier);
+ } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ {
+ ret = hidpp10_enable_fast_scrolling(hidpp);
+ multiplier = 8;
+ }
+ if (ret)
+ return ret;
+
+ hidpp->vertical_wheel_counter.resolution_multiplier = multiplier;
+ hidpp->vertical_wheel_counter.mm256_per_hi_res_unit =
+ hi_res_scroll_look_up_mm256(hidpp->hid_dev->product);
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
@@ -2572,6 +2763,11 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
wtp_populate_input(hidpp, input, origin_is_hid_core);
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
m560_populate_input(hidpp, input, origin_is_hid_core);
+
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) {
+ input_set_capability(input, EV_REL, REL_WHEEL_HI_RES);
+ hidpp->vertical_wheel_counter.dev = input;
+ }
}
static int hidpp_input_configured(struct hid_device *hdev,
@@ -2690,6 +2886,25 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}
+static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter;
+
+ /* A scroll event may occur before the multiplier has been retrieved or
+ * the input device set, or high-res scroll enabling may fail. In such
+ * cases we must return early (falling back to default behaviour) to
+ * avoid a crash in hid_scroll_counter_handle_scroll.
+ */
+ if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
+ || counter->dev == NULL || counter->resolution_multiplier == 0)
+ return 0;
+
+ hid_scroll_counter_handle_scroll(counter, value);
+ return 1;
+}
+
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
{
static atomic_t battery_no = ATOMIC_INIT(0);
@@ -2901,6 +3116,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
+ hi_res_scroll_enable(hidpp);
+
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
/* if the input nodes are already created, we can stop now */
return;
@@ -3086,35 +3304,97 @@ static void hidpp_remove(struct hid_device *hdev)
mutex_destroy(&hidpp->send_mutex);
}
+#define LDJ_DEVICE(product) \
+ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
+ USB_VENDOR_ID_LOGITECH, (product))
+
static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4011),
+ LDJ_DEVICE(0x4011),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
{ /* wireless touchpad T650 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4101),
+ LDJ_DEVICE(0x4101),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
{ /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
+ { /* Mouse Logitech Anywhere MX */
+ LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+ { /* Mouse Logitech Cube */
+ LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+ { /* Mouse Logitech M335 */
+ LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech M336, M337, and M535 */
+ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT1),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT2),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech M515 */
+ LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
{ /* Mouse logitech M560 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x402d),
- .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
+ LDJ_DEVICE(0x402d),
+ .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
+ | HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+ { /* Mouse Logitech M705 (firmware RQM17) */
+ LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+ { /* Mouse Logitech M705 (firmware RQM67) */
+ LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech M720 */
+ LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_M720_BT),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Anywhere 2 */
+ LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT1),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT2),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT3),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Anywhere 2S */
+ LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2S_BT),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Master */
+ LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT1),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT2),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT3),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Master 2S */
+ LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_2S_BT),
+ .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech Performance MX */
+ LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4024),
+ LDJ_DEVICE(0x4024),
.driver_data = HIDPP_QUIRK_CLASS_K400 },
{ /* Solar Keyboard Logitech K750 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4002),
+ LDJ_DEVICE(0x4002),
.driver_data = HIDPP_QUIRK_CLASS_K750 },
- { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
+ { LDJ_DEVICE(HID_ANY_ID) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
@@ -3123,12 +3403,19 @@ static const struct hid_device_id hidpp_devices[] = {
MODULE_DEVICE_TABLE(hid, hidpp_devices);
+static const struct hid_usage_id hidpp_usages[] = {
+ { HID_GD_WHEEL, EV_REL, REL_WHEEL },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
static struct hid_driver hidpp_driver = {
.name = "logitech-hidpp-device",
.id_table = hidpp_devices,
.probe = hidpp_probe,
.remove = hidpp_remove,
.raw_event = hidpp_raw_event,
+ .usage_table = hidpp_usages,
+ .event = hidpp_event,
.input_configured = hidpp_input_configured,
.input_mapping = hidpp_input_mapping,
.input_mapped = hidpp_input_mapped,
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 249d49b6b16c..7926c275f258 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -463,6 +463,17 @@ static const struct hid_device_id hid_have_special_driver[] = {
#endif
#if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT1) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_M336_337_535_BT2) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_M720_BT) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT1) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT2) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2_BT3) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_ANYWHERE_2S_BT) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT1) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT2) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_BT3) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_MX_MASTER_2S_BT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
#endif
#if IS_ENABLED(CONFIG_HID_LOGITECH_DJ)
--
2.18.0.1017.ga543ac7ca45-goog
^ permalink raw reply related
* [PATCH 2/3] Create a utility class for counting scroll events
From: Harry Cutts @ 2018-08-23 18:30 UTC (permalink / raw)
To: linux-input, LKML
Cc: Jiri Kosina, Dmitry Torokhov, Benjamin Tissoires, Harry Cutts,
Jiri Kosina
In-Reply-To: <20180823183057.247630-1-hcutts@chromium.org>
To avoid code duplication, this class counts high-resolution scroll
movements and emits the legacy low-resolution events when appropriate.
Drivers should create one instance for each scroll wheel that they
need to handle.
Signed-off-by: Harry Cutts <hcutts@chromium.org>
---
drivers/hid/hid-input.c | 44 +++++++++++++++++++++++++++++++++++++++++
include/linux/hid.h | 28 ++++++++++++++++++++++++++
2 files changed, 72 insertions(+)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 4e94ea3e280a..4ee23b297472 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1826,3 +1826,47 @@ void hidinput_disconnect(struct hid_device *hid)
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);
+/**
+ * hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
+ * events given a high-resolution wheel
+ * movement.
+ * @counter: a hid_scroll_counter struct describing the wheel.
+ * @hi_res_value: the movement of the wheel, in the mouse's high-resolution
+ * units.
+ *
+ * Given a high-resolution movement, this function converts the movement into
+ * 256ths of a millimeter and emits high-resolution scroll events for the input
+ * device. It also uses the multiplier from &struct hid_scroll_counter to emit
+ * low-resolution scroll events when appropriate for backwards-compatibility
+ * with userspace input libraries.
+ */
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+ int hi_res_value)
+{
+ int low_res_scroll_amount;
+ /* Some wheels often rest 7/8ths of a notch from the previous notch
+ * after slow movement, so we want the threshold for low-res events in
+ * between the notches (e.g. after 4/8ths) as opposed to on the notches
+ * themselves (8/8ths).
+ */
+ int threshold = counter->resolution_multiplier / 2;
+
+ input_report_rel(counter->dev, REL_WHEEL_HI_RES,
+ hi_res_value * counter->mm256_per_hi_res_unit);
+
+ counter->remainder += hi_res_value;
+ if (abs(counter->remainder) >= threshold) {
+ /* Add (or subtract) 1 because we want to trigger when half-way
+ * to the next notch (i.e. scroll 1 notch after a 1/2 notch
+ * movement, 2 notches after a 1 1/2 notch movement, etc.).
+ */
+ low_res_scroll_amount =
+ counter->remainder / counter->resolution_multiplier
+ + (hi_res_value > 0 ? 1 : -1);
+ input_report_rel(counter->dev, REL_WHEEL,
+ low_res_scroll_amount);
+ counter->remainder -=
+ low_res_scroll_amount * counter->resolution_multiplier;
+ }
+}
+EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 834e6461a690..b7bc0b2faf4f 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1138,6 +1138,34 @@ static inline u32 hid_report_len(struct hid_report *report)
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int interrupt);
+
+/**
+ * struct hid_scroll_counter - Utility class for processing high-resolution
+ * scroll events.
+ * @dev: the input device for which events should be reported.
+ * @mm256_per_hi_res_unit: the amount moved by the user's finger for each
+ * high-resolution unit reported by the mouse, in 256ths
+ * of a millimetre.
+ * @resolution_multiplier: the wheel's resolution in high-resolution mode as a
+ * multiple of its lower resolution. For example, if
+ * moving the wheel by one "notch" would result in a
+ * value of 1 in low-resolution mode but 8 in
+ * high-resolution, the multiplier is 8.
+ * @remainder: counts the number of high-resolution units moved since the last
+ * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
+ * only be used by class methods.
+ */
+struct hid_scroll_counter {
+ struct input_dev *dev;
+ int mm256_per_hi_res_unit;
+ int resolution_multiplier;
+
+ int remainder;
+};
+
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+ int hi_res_value);
+
/* HID quirks API */
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
int hid_quirks_init(char **quirks_param, __u16 bus, int count);
--
2.18.0.1017.ga543ac7ca45-goog
^ permalink raw reply related
* [PATCH 1/3] Add the `REL_WHEEL_HI_RES` event code
From: Harry Cutts @ 2018-08-23 18:30 UTC (permalink / raw)
To: linux-input, LKML
Cc: Jiri Kosina, Dmitry Torokhov, Benjamin Tissoires, Harry Cutts,
linux-doc, Jonathan Corbet
In-Reply-To: <20180823183057.247630-1-hcutts@chromium.org>
This event code represents scroll reports from high-resolution wheels,
and will be used by future patches in this series. See the linux-input
"Reporting high-resolution scroll events" thread [0] for more details.
[0]: https://www.spinics.net/lists/linux-input/msg57380.html
Signed-off-by: Harry Cutts <hcutts@chromium.org>
---
Documentation/input/event-codes.rst | 11 ++++++++++-
include/uapi/linux/input-event-codes.h | 1 +
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
index a8c0873beb95..df024cb82829 100644
--- a/Documentation/input/event-codes.rst
+++ b/Documentation/input/event-codes.rst
@@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
* REL_WHEEL, REL_HWHEEL:
- These codes are used for vertical and horizontal scroll wheels,
- respectively.
+ respectively. The value is the number of "notches" moved on the wheel, the
+ physical size of which varies by device. For high-resolution wheels (which
+ report multiple events for each notch of movement, or do not have notches)
+ this may be an approximation based on the high-resolution scroll events.
+
+* REL_WHEEL_HI_RES:
+
+ - If a vertical scroll wheel supports high-resolution scrolling, this code
+ will be emitted in addition to REL_WHEEL. The value is the (approximate)
+ distance travelled by the user's finger, in 256ths of a millimeter.
EV_ABS
------
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 53fbae27b280..dad8d3890a3a 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -708,6 +708,7 @@
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
+#define REL_WHEEL_HI_RES 0x0a
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
--
2.18.0.1017.ga543ac7ca45-goog
^ permalink raw reply related
* [PATCH 0/3] Add support for high-resolution scrolling on Logitech mice
From: Harry Cutts @ 2018-08-23 18:30 UTC (permalink / raw)
To: linux-input, LKML
Cc: Jiri Kosina, Dmitry Torokhov, Benjamin Tissoires, Harry Cutts,
linux-doc, Jonathan Corbet, Jiri Kosina
Hi everyone,
This set of patches adds support for high-resolution scroll wheels on
Logitech mice. See the linux-input "Reporting high-resolution scroll
events" thread [0] for previous discussion of the evdev changes. I would
love to hear your feedback.
Thanks,
Harry Cutts
Chrome OS Touch/Input team
[0]: https://www.spinics.net/lists/linux-input/msg57380.html
Harry Cutts (3):
Add the `REL_WHEEL_HI_RES` event code
Create a utility class for counting scroll events
Enable high-resolution scrolling on Logitech mice
Documentation/input/event-codes.rst | 11 +-
drivers/hid/hid-ids.h | 15 ++
drivers/hid/hid-input.c | 44 ++++
drivers/hid/hid-logitech-hidpp.c | 341 +++++++++++++++++++++++--
drivers/hid/hid-quirks.c | 11 +
include/linux/hid.h | 28 ++
include/uapi/linux/input-event-codes.h | 1 +
7 files changed, 423 insertions(+), 28 deletions(-)
--
2.18.0.1017.ga543ac7ca45-goog
^ permalink raw reply
* [PATCH] HID: add support for Apple Magic Keyboards
From: Sean O'Brien @ 2018-08-23 16:40 UTC (permalink / raw)
To: Jiri Kosina
Cc: Dmitry Torokhov, Benjamin Tissoires, linux-kernel, linux-input
USB device
Vendor 05ac (Apple)
Device 026c (Magic Keyboard with Numeric Keypad)
Bluetooth devices
Vendor 004c (Apple)
Device 0267 (Magic Keyboard)
Device 026c (Magic Keyboard with Numeric Keypad)
Support already exists for the Magic Keyboard over USB connection.
This patch adds support for the Magic Keyboard over Bluetooth
connection, and for the Magic Keyboard with Numeric Keypad over
Bluetooth and USB connection.
Signed-off-by: Sean O'Brien <seobrien@chromium.org>
---
drivers/hid/hid-apple.c | 9 ++++++++-
drivers/hid/hid-ids.h | 2 ++
drivers/hid/hid-quirks.c | 3 +++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 25b7bd56ae11..1cb41992aaa1 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -335,7 +335,8 @@ static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- if (usage->hid == (HID_UP_CUSTOM | 0x0003)) {
+ if (usage->hid == (HID_UP_CUSTOM | 0x0003) ||
+ usage->hid == (HID_UP_MSVENDOR | 0x0003)) {
/* The fn key on Apple USB keyboards */
set_bit(EV_REP, hi->input->evbit);
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
@@ -472,6 +473,12 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI),
.driver_data = APPLE_HAS_FN },
+ { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI),
+ .driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 79bdf0c7e351..5dbe3fb82690 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -88,6 +88,7 @@
#define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101
#define USB_VENDOR_ID_APPLE 0x05ac
+#define BT_VENDOR_ID_APPLE 0x004c
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
@@ -157,6 +158,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257
#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI 0x0267
+#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI 0x026c
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 249d49b6b16c..a3b3aecf8628 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -270,6 +270,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI) },
+ { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI) },
+ { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
#endif
--
2.18.0.1017.ga543ac7ca45-goog
^ permalink raw reply related
* Re: [PATCH V4 3/3] Input: new da7280 haptic driver
From: Randy Dunlap @ 2018-08-23 15:28 UTC (permalink / raw)
To: Roy Im, Dmitry Torokhov, Rob Herring
Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
linux-kernel
In-Reply-To: <2c28a93093c075e0145b70e7bc999f2654d0431d.1534830272.git.Roy.Im@diasemi.com>
On 08/20/2018 10:44 PM, Roy Im wrote:
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index ca59a2b..6e0de69 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -851,4 +851,16 @@ config INPUT_SC27XX_VIBRA
> To compile this driver as a module, choose M here. The module will
> be called sc27xx_vibra.
>
Hi,
Can you explain the "depends on" below, please.
> +config INPUT_DA7280_HAPTICS
> + tristate "Dialog Semiconductor DA7280 haptics support"
> + depends on (INPUT && I2C && SYSFS) || PWM
> + select INPUT_FF_MEMLESS
> + select REGMAP_I2C
If INPUT is disabled, and I2C is disabled, and SYSFS is disabled, but PWM=y,
then the "select"s will have problems with unmet dependencies.
> + help
> + Say Y to enable support for the haptics controller on
> + Dialog DA7280 chip.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called da7280-haptic.
That module name doesn't match what's in the Makefile (below).
> +
> 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
--
~Randy
^ permalink raw reply
* INFO: rcu detected stall in mousedev_write
From: syzbot @ 2018-08-23 14:35 UTC (permalink / raw)
To: dmitry.torokhov, linux-input, linux-kernel, syzkaller-bugs
Hello,
syzbot found the following crash on:
HEAD commit: 899fbc33fd77 Merge tag 'platform-drivers-x86-v4.19-1' of g..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=11a98c0a400000
kernel config: https://syzkaller.appspot.com/x/.config?x=e408e8a7bf8306cb
dashboard link: https://syzkaller.appspot.com/bug?extid=9436b02171ac0894d33e
compiler: gcc (GCC) 8.0.1 20180413 (experimental)
Unfortunately, I don't have any reproducer for this crash yet.
IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+9436b02171ac0894d33e@syzkaller.appspotmail.com
rcu: INFO: rcu_sched self-detected stall on CPU
rcu: 1-....: (104999 ticks this GP) idle=19e/1/0x4000000000000002
softirq=112269/112269 fqs=19579
rcu: (t=105000 jiffies g=225813 q=1973)
NMI backtrace for cpu 1
CPU: 1 PID: 21558 Comm: syz-executor5 Not tainted 4.18.0+ #203
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
Google 01/01/2011
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1c9/0x2b4 lib/dump_stack.c:113
nmi_cpu_backtrace.cold.3+0x48/0x88 lib/nmi_backtrace.c:101
nmi_trigger_cpumask_backtrace+0x151/0x192 lib/nmi_backtrace.c:62
arch_trigger_cpumask_backtrace+0x14/0x20 arch/x86/kernel/apic/hw_nmi.c:38
trigger_single_cpu_backtrace include/linux/nmi.h:162 [inline]
rcu_dump_cpu_stacks+0x175/0x1c2 kernel/rcu/tree.c:1340
print_cpu_stall.cold.78+0x2fb/0x59c kernel/rcu/tree.c:1478
check_cpu_stall kernel/rcu/tree.c:1550 [inline]
__rcu_pending kernel/rcu/tree.c:3276 [inline]
rcu_pending kernel/rcu/tree.c:3319 [inline]
rcu_check_callbacks+0xd4a/0x15a0 kernel/rcu/tree.c:2665
update_process_times+0x2d/0x70 kernel/time/timer.c:1636
tick_sched_handle+0x9f/0x180 kernel/time/tick-sched.c:164
tick_sched_timer+0x45/0x130 kernel/time/tick-sched.c:1274
__run_hrtimer kernel/time/hrtimer.c:1398 [inline]
__hrtimer_run_queues+0x3eb/0xff0 kernel/time/hrtimer.c:1460
hrtimer_interrupt+0x2f3/0x750 kernel/time/hrtimer.c:1518
local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1029 [inline]
smp_apic_timer_interrupt+0x16d/0x6a0 arch/x86/kernel/apic/apic.c:1054
apic_timer_interrupt+0xf/0x20 arch/x86/entry/entry_64.S:864
</IRQ>
RIP: 0010:arch_local_irq_enable arch/x86/include/asm/paravirt.h:793 [inline]
RIP: 0010:__raw_spin_unlock_irq include/linux/spinlock_api_smp.h:168
[inline]
RIP: 0010:_raw_spin_unlock_irq+0x56/0x70 kernel/locking/spinlock.c:192
Code: f1 87 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00
75 1d 48 83 3d db 98 3d 01 00 74 11 fb 66 0f 1f 44 00 00 <65> ff 0d a3 c9
4d 79 5b 5d c3 0f 0b e8 09 8f 07 fb eb dc 0f 1f 80
RSP: 0018:ffff88018eb8faf8 EFLAGS: 00000286 ORIG_RAX: ffffffffffffff13
RAX: dffffc0000000000 RBX: ffff8801bff42be8 RCX: 0000000000000000
RDX: 1ffffffff0fe379f RSI: 0000000000000001 RDI: ffffffff87f1bcf8
RBP: ffff88018eb8fb00 R08: ffff88018d902000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000c6c5ef
R13: dffffc0000000000 R14: 0000000000c6c5ef R15: ffff8801bff42be8
spin_unlock_irq include/linux/spinlock.h:379 [inline]
mousedev_write+0x19d/0x840 drivers/input/mousedev.c:709
__vfs_write+0x117/0x9d0 fs/read_write.c:485
vfs_write+0x1fc/0x560 fs/read_write.c:549
ksys_write+0x101/0x260 fs/read_write.c:598
__do_sys_write fs/read_write.c:610 [inline]
__se_sys_write fs/read_write.c:607 [inline]
__x64_sys_write+0x73/0xb0 fs/read_write.c:607
do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x457089
Code: fd b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7
48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff
ff 0f 83 cb b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f0d92208c78 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f0d922096d4 RCX: 0000000000457089
RDX: 00000000ffffffd1 RSI: 00000000200001c0 RDI: 0000000000000005
RBP: 00000000009300a0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000ffffffff
R13: 00000000004d7088 R14: 00000000004ca379 R15: 0000000000000000
---
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.
^ permalink raw reply
* Re: [PATCH v4 1/3] Input: edt-ft5x06 - Add support for regulator
From: Dmitry Torokhov @ 2018-08-23 0:48 UTC (permalink / raw)
To: Mylène Josserand
Cc: robh+dt, mark.rutland, maxime.ripard, wens, linux-input,
devicetree, linux-kernel, linux-arm-kernel, thomas.petazzoni,
Lothar Waßmann, Simon Budig, Franklin S Cooper Jr
In-Reply-To: <20180807085944.337bf20b@dell-desktop.home>
On Tue, Aug 07, 2018 at 08:59:44AM +0200, Mylène Josserand wrote:
> Hello Dmitry,
>
> Thank you again for the review.
>
> On Wed, 25 Jul 2018 17:47:32 -0700
> Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>
> > Hi Mylène,
> >
> > On Wed, Jul 25, 2018 at 09:34:08AM +0200, Mylène Josserand wrote:
> > > Add the support of regulator to use it as VCC source.
> > >
> > > Signed-off-by: Mylène Josserand <mylene.josserand@bootlin.com>
> > > Reviewed-by: Rob Herring <robh@kernel.org>
> > > ---
> > > .../bindings/input/touchscreen/edt-ft5x06.txt | 1 +
> > > drivers/input/touchscreen/edt-ft5x06.c | 43 ++++++++++++++++++++++
> > > 2 files changed, 44 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
> > > index 025cf8c9324a..48e975b9c1aa 100644
> > > --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
> > > +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
> > > @@ -30,6 +30,7 @@ Required properties:
> > > Optional properties:
> > > - reset-gpios: GPIO specification for the RESET input
> > > - wake-gpios: GPIO specification for the WAKE input
> > > + - vcc-supply: Regulator that supplies the touchscreen
> > >
> > > - pinctrl-names: should be "default"
> > > - pinctrl-0: a phandle pointing to the pin settings for the
> > > diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
> > > index 1e18ca0d1b4e..dcde719094f7 100644
> > > --- a/drivers/input/touchscreen/edt-ft5x06.c
> > > +++ b/drivers/input/touchscreen/edt-ft5x06.c
> > > @@ -39,6 +39,7 @@
> > > #include <linux/input/mt.h>
> > > #include <linux/input/touchscreen.h>
> > > #include <linux/of_device.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > > #define WORK_REGISTER_THRESHOLD 0x00
> > > #define WORK_REGISTER_REPORT_RATE 0x08
> > > @@ -91,6 +92,7 @@ struct edt_ft5x06_ts_data {
> > > struct touchscreen_properties prop;
> > > u16 num_x;
> > > u16 num_y;
> > > + struct regulator *vcc;
> > >
> > > struct gpio_desc *reset_gpio;
> > > struct gpio_desc *wake_gpio;
> > > @@ -963,6 +965,13 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
> > > }
> > > }
> > >
> > > +static void edt_ft5x06_disable_regulator(void *arg)
> > > +{
> > > + struct edt_ft5x06_ts_data *data = arg;
> > > +
> > > + regulator_disable(data->vcc);
> > > +}
> > > +
> > > static int edt_ft5x06_ts_probe(struct i2c_client *client,
> > > const struct i2c_device_id *id)
> > > {
> > > @@ -991,6 +1000,28 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
> > >
> > > tsdata->max_support_points = chip_data->max_support_points;
> > >
> > > + tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
> > > + if (IS_ERR(tsdata->vcc)) {
> > > + error = PTR_ERR(tsdata->vcc);
> > > + if (error != -EPROBE_DEFER)
> > > + dev_err(&client->dev, "failed to request regulator: %d\n",
> > > + error);
> > > + return error;
> > > + }
> > > +
> > > + error = regulator_enable(tsdata->vcc);
> > > + if (error < 0) {
> > > + dev_err(&client->dev, "failed to enable vcc: %d\n",
> > > + error);
> > > + return error;
> > > + }
> >
> > It is better to put the chip into reset and then power up the regulatori
> > and take it out of the reset, rather than power up and then toggle reset
> > on and off.
>
> okay, thanks, I will update it.
>
> >
> > > +
> > > + error = devm_add_action_or_reset(&client->dev,
> > > + edt_ft5x06_disable_regulator,
> > > + tsdata);
> > > + if (error)
> > > + return error;
> > > +
> > > tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
> > > "reset", GPIOD_OUT_HIGH);
> > > if (IS_ERR(tsdata->reset_gpio)) {
> > > @@ -1120,9 +1151,12 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client)
> > > static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
> > > {
> > > struct i2c_client *client = to_i2c_client(dev);
> > > + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
> > >
> > > if (device_may_wakeup(dev))
> > > enable_irq_wake(client->irq);
> > > + else
> > > + regulator_disable(tsdata->vcc);
> > >
> > > return 0;
> > > }
> > > @@ -1130,9 +1164,18 @@ static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
> > > static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
> > > {
> > > struct i2c_client *client = to_i2c_client(dev);
> > > + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
> > > + int ret;
> > >
> > > if (device_may_wakeup(dev))
> > > disable_irq_wake(client->irq);
> > > + else {
> > > + ret = regulator_enable(tsdata->vcc);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to enable vcc: %d\n", ret);
> > > + return ret;
> > > + }
> > > + }
> >
> > I believe I mentioned in other review that once you powered up the
> > device, you need to restore its settings, include switching to factory
> > mode, if it was in factory mode, and restoring threshold/gain/offset
> > settings.
>
> Yes, I will update the driver with that but as I told you in a previous
> mail, I can't test the suspend/resume :(
I wonder if Simon or Lothar or Franklin could help us with it (CCed). If
not, then we'll have to go by review only.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2 1/2] Input: atmel_mxt_ts: Add support for optional regulators.
From: George G. Davis @ 2018-08-22 22:53 UTC (permalink / raw)
To: Nick Dyer
Cc: Paweł Chmiel, mark.rutland, devicetree, alexandre.belloni,
dmitry.torokhov, linux-kernel, robh+dt, linux-input,
linux-arm-kernel
In-Reply-To: <20180717210005.GA21416@hairyalien>
Hello Nick,
On Tue, Jul 17, 2018 at 10:00:05PM +0100, Nick Dyer wrote:
> On Tue, Jul 17, 2018 at 08:16:25PM +0200, Paweł Chmiel wrote:
> > This patch adds optional regulators, which can be used to power
> > up touchscreen. After enabling regulators, we need to wait 150msec.
> > This value is taken from official driver.
> >
> > It was tested on Samsung Galaxy i9000 (based on Samsung S5PV210 SOC).
> >
> > Signed-off-by: Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
> > ---
> > Changes from v1:
> > - Enable regulators only if reset_gpio is present.
> > - Switch from devm_regulator_get_optional to devm_regulator_get
> > ---
> > drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++++++++++++++++++++--
> > 1 file changed, 44 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> > index 54fe190fd4bc..005f0fee9fc8 100644
> > --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> > +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> > @@ -27,6 +27,7 @@
> > #include <linux/interrupt.h>
> > #include <linux/of.h>
> > #include <linux/property.h>
> > +#include <linux/regulator/consumer.h>
> > #include <linux/slab.h>
> > #include <linux/gpio/consumer.h>
> > #include <linux/property.h>
> > @@ -194,10 +195,10 @@ enum t100_type {
> >
> > /* Delay times */
> > #define MXT_BACKUP_TIME 50 /* msec */
> > -#define MXT_RESET_GPIO_TIME 20 /* msec */
> > #define MXT_RESET_INVALID_CHG 100 /* msec */
> > #define MXT_RESET_TIME 200 /* msec */
> > #define MXT_RESET_TIMEOUT 3000 /* msec */
> > +#define MXT_REGULATOR_DELAY 150 /* msec */
> > #define MXT_CRC_TIMEOUT 1000 /* msec */
> > #define MXT_FW_RESET_TIME 3000 /* msec */
> > #define MXT_FW_CHG_TIMEOUT 300 /* msec */
> > @@ -310,6 +311,8 @@ struct mxt_data {
> > struct t7_config t7_cfg;
> > struct mxt_dbg dbg;
> > struct gpio_desc *reset_gpio;
> > + struct regulator *vdd_reg;
> > + struct regulator *avdd_reg;
> >
> > /* Cached parameters from object table */
> > u16 T5_address;
> > @@ -3076,6 +3079,22 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
> > return error;
> > }
> >
> > + data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
> > + if (IS_ERR(data->vdd_reg)) {
> > + error = PTR_ERR(data->vdd_reg);
> > + dev_err(&client->dev, "Failed to get vdd regulator: %d\n",
> > + error);
> > + return error;
> > + }
> > +
> > + data->avdd_reg = devm_regulator_get(&client->dev, "avdd");
> > + if (IS_ERR(data->avdd_reg)) {
> > + error = PTR_ERR(data->avdd_reg);
> > + dev_err(&client->dev, "Failed to get avdd regulator: %d\n",
> > + error);
> > + return error;
> > + }
> > +
> > error = devm_request_threaded_irq(&client->dev, client->irq,
> > NULL, mxt_interrupt, IRQF_ONESHOT,
> > client->name, data);
> > @@ -3087,7 +3106,26 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
> > disable_irq(client->irq);
> >
> > if (data->reset_gpio) {
> > - msleep(MXT_RESET_GPIO_TIME);
> > + error = regulator_enable(data->vdd_reg);
> > + if (error) {
> > + dev_err(&client->dev, "Failed to enable vdd regulator: %d\n",
> > + error);
> > + return error;
> > + }
> > +
> > + error = regulator_enable(data->avdd_reg);
> > + if (error) {
> > + dev_err(&client->dev, "Failed to enable avdd regulator: %d\n",
> > + error);
> > + return error;
> > + }
> > +
> > + /*
> > + * According to maXTouch power sequencing specification, RESET line
> > + * must be kept low until some time after regulators come up to
> > + * voltage
> > + */
> > + msleep(MXT_REGULATOR_DELAY);
> > gpiod_set_value(data->reset_gpio, 1);
> > msleep(MXT_RESET_INVALID_CHG);
>
> Hi Pawel-
>
> I see you've borrowed some of the logic from the patch I wrote a while
> back (see https://github.com/ndyer/linux/commit/8e9687e41ed062 )
I'm actually quite interested in your work and notice that it has
languished for some time now. If you have rebased any of your earlier
work to more recent kernel versions, I'd be happy to assist with testing
and review.
--
Regards,
George
> The correct behaviour according to Atmel should be:
>
> * Make RESET zero
> * Bring up regulators
> * Wait for a period to settle (150 msec)
> * Release RESET
> * Wait for 100 msec whilst device gets through bootloader
> * Wait for CHG line assert before reading info block
>
> I can't see the first and last steps in your patch at present.
>
> The only downside with this approach is that there are a lot of
> delays during driver probe, but I believe the asynchronous probe stuff
> that's landed since I wrote the original patch should address that.
>
> cheers
>
> Nick
>
> > }
> > @@ -3116,6 +3154,10 @@ static int mxt_remove(struct i2c_client *client)
> > struct mxt_data *data = i2c_get_clientdata(client);
> >
> > disable_irq(data->irq);
> > + if (data->reset_gpio) {
> > + regulator_disable(data->avdd_reg);
> > + regulator_disable(data->vdd_reg);
> > + }
> > sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
> > mxt_free_input_device(data);
> > mxt_free_object_table(data);
> > --
> > 2.7.4
> >
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 2/3] Input: atmel_mxt_ts: Wait for device be ready for communication
From: George G. Davis @ 2018-08-22 18:08 UTC (permalink / raw)
To: Paweł Chmiel
Cc: nick, mark.rutland, devicetree, alexandre.belloni,
dmitry.torokhov, linux-kernel, robh+dt, linux-input,
linux-arm-kernel
In-Reply-To: <1533050291-11502-3-git-send-email-pawel.mikolaj.chmiel@gmail.com>
Hello Paweł,
On Tue, Jul 31, 2018 at 05:18:10PM +0200, Paweł Chmiel wrote:
> According to documentation, device isn't ready for communication,
> until firmware asserts the CHG line. Add missing wait for this.
>
> Signed-off-by: Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 2cd7f6db6ba9..79e08916359f 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -202,6 +202,7 @@ enum t100_type {
> #define MXT_CRC_TIMEOUT 1000 /* msec */
> #define MXT_FW_RESET_TIME 3000 /* msec */
> #define MXT_FW_CHG_TIMEOUT 300 /* msec */
> +#define MXT_POWERON_DELAY 150 /* msec */
>
> /* Command to unlock bootloader */
> #define MXT_UNLOCK_CMD_MSB 0xaa
> @@ -3046,6 +3047,16 @@ static int mxt_regulator_enable(struct mxt_data *data)
> msleep(MXT_REGULATOR_DELAY);
> gpiod_set_value(data->reset_gpio, 1);
> msleep(MXT_RESET_INVALID_CHG);
> +
> +retry_wait:
> + reinit_completion(&data->bl_completion);
> + data->in_bootloader = true;
> + error = mxt_wait_for_completion(data, &data->bl_completion,
> + MXT_POWERON_DELAY);
Please fix the following checkpatch issues:
drivers/input/touchscreen/atmel_mxt_ts.c:3078: WARNING:LONG_LINE: line over 80 characters
drivers/input/touchscreen/atmel_mxt_ts.c:3078: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
Thanks!
--
Regards,
George
> + if (error == -EINTR)
> + goto retry_wait;
> +
> + data->in_bootloader = false;
> }
>
> return 0;
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 1/3] Input: atmel_mxt_ts: Add support for optional regulators.
From: George G. Davis @ 2018-08-22 18:06 UTC (permalink / raw)
To: Paweł Chmiel
Cc: nick, mark.rutland, devicetree, alexandre.belloni,
dmitry.torokhov, linux-kernel, robh+dt, linux-input,
linux-arm-kernel
In-Reply-To: <1533050291-11502-2-git-send-email-pawel.mikolaj.chmiel@gmail.com>
Hello Paweł,
I would suggest dropping sentence punctuation in the patch subject line:
Input: atmel_mxt_ts: Add support for optional regulators.
More below...
On Tue, Jul 31, 2018 at 05:18:09PM +0200, Paweł Chmiel wrote:
> This patch adds optional regulators, which can be used to power
> up touchscreen. After enabling regulators, we need to wait 150msec.
> This value is taken from official driver.
>
> It was tested on Samsung Galaxy i9000 (based on Samsung S5PV210 SOC).
>
> Signed-off-by: Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
> ---
> Changes from v2:
> - Move code enabling regulators into separate method,
> to make code more readable.
>
> Changes from v1:
> - Enable regulators only if reset_gpio is present.
> - Switch from devm_regulator_get_optional to devm_regulator_get
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 65 +++++++++++++++++++++++++++++---
> 1 file changed, 59 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 54fe190fd4bc..2cd7f6db6ba9 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -27,6 +27,7 @@
> #include <linux/interrupt.h>
> #include <linux/of.h>
> #include <linux/property.h>
> +#include <linux/regulator/consumer.h>
> #include <linux/slab.h>
> #include <linux/gpio/consumer.h>
> #include <linux/property.h>
> @@ -194,10 +195,10 @@ enum t100_type {
>
> /* Delay times */
> #define MXT_BACKUP_TIME 50 /* msec */
> -#define MXT_RESET_GPIO_TIME 20 /* msec */
> #define MXT_RESET_INVALID_CHG 100 /* msec */
> #define MXT_RESET_TIME 200 /* msec */
> #define MXT_RESET_TIMEOUT 3000 /* msec */
> +#define MXT_REGULATOR_DELAY 150 /* msec */
> #define MXT_CRC_TIMEOUT 1000 /* msec */
> #define MXT_FW_RESET_TIME 3000 /* msec */
> #define MXT_FW_CHG_TIMEOUT 300 /* msec */
> @@ -310,6 +311,8 @@ struct mxt_data {
> struct t7_config t7_cfg;
> struct mxt_dbg dbg;
> struct gpio_desc *reset_gpio;
> + struct regulator *vdd_reg;
> + struct regulator *avdd_reg;
>
> /* Cached parameters from object table */
> u16 T5_address;
> @@ -3016,6 +3019,38 @@ static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
> { }
> };
>
> +static int mxt_regulator_enable(struct mxt_data *data)
> +{
> + int error;
> +
> + if (data->reset_gpio) {
> + error = regulator_enable(data->vdd_reg);
> + if (error) {
> + dev_err(&data->client->dev, "Failed to enable vdd regulator: %d\n",
> + error);
> + return error;
> + }
> +
> + error = regulator_enable(data->avdd_reg);
> + if (error) {
> + dev_err(&data->client->dev, "Failed to enable avdd regulator: %d\n",
> + error);
> + return error;
> + }
> +
> + /*
> + * According to maXTouch power sequencing specification, RESET line
Please fix the following checkpatch issue:
drivers/input/touchscreen/atmel_mxt_ts.c:3065: WARNING:LONG_LINE_COMMENT: line over 80 characters
Thanks!
--
Regards,
George
> + * must be kept low until some time after regulators come up to
> + * voltage
> + */
> + msleep(MXT_REGULATOR_DELAY);
> + gpiod_set_value(data->reset_gpio, 1);
> + msleep(MXT_RESET_INVALID_CHG);
> + }
> +
> + return 0;
> +}
> +
> static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
> {
> struct mxt_data *data;
> @@ -3076,6 +3111,22 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
> return error;
> }
>
> + data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
> + if (IS_ERR(data->vdd_reg)) {
> + error = PTR_ERR(data->vdd_reg);
> + dev_err(&client->dev, "Failed to get vdd regulator: %d\n",
> + error);
> + return error;
> + }
> +
> + data->avdd_reg = devm_regulator_get(&client->dev, "avdd");
> + if (IS_ERR(data->avdd_reg)) {
> + error = PTR_ERR(data->avdd_reg);
> + dev_err(&client->dev, "Failed to get avdd regulator: %d\n",
> + error);
> + return error;
> + }
> +
> error = devm_request_threaded_irq(&client->dev, client->irq,
> NULL, mxt_interrupt, IRQF_ONESHOT,
> client->name, data);
> @@ -3086,11 +3137,9 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
>
> disable_irq(client->irq);
>
> - if (data->reset_gpio) {
> - msleep(MXT_RESET_GPIO_TIME);
> - gpiod_set_value(data->reset_gpio, 1);
> - msleep(MXT_RESET_INVALID_CHG);
> - }
> + error = mxt_regulator_enable(data);
> + if (error)
> + return error;
>
> error = mxt_initialize(data);
> if (error)
> @@ -3116,6 +3165,10 @@ static int mxt_remove(struct i2c_client *client)
> struct mxt_data *data = i2c_get_clientdata(client);
>
> disable_irq(data->irq);
> + if (data->reset_gpio) {
> + regulator_disable(data->avdd_reg);
> + regulator_disable(data->vdd_reg);
> + }
> sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
> mxt_free_input_device(data);
> mxt_free_object_table(data);
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver
From: Pascal PAILLET-LME @ 2018-08-21 12:23 UTC (permalink / raw)
To: Enric Balletbo Serra
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Lee Jones,
Liam Girdwood, Mark Brown, wim@linux-watchdog.org, Guenter Roeck,
linux-input@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel, linux-watchdog@vger.kernel.org,
benjamin.gaignard@linaro.org
In-Reply-To: <CAFqH_51hua9S-_Hp5iKTT_UB2Dgdb6HhDkD1G4=NFq_2qvvJKA@mail.gmail.com>
Hi,
Thanks a lot for the review ! I just have a question below regarding
populate method.
Le 07/10/2018 12:38 AM, Enric Balletbo Serra a écrit :
> Hi Pascal,
>
> Thanks for the patch some comments below.
>
> Missatge de Pascal PAILLET-LME <p.paillet@st.com> del dia dj., 5 de
> jul. 2018 a les 17:17:
>> From: pascal paillet <p.paillet@st.com>
>>
>> stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
>> regulators and 3 switches with various capabilities.
>>
>> Signed-off-by: pascal paillet <p.paillet@st.com>
>> ---
>> drivers/mfd/Kconfig | 14 ++
>> drivers/mfd/Makefile | 1 +
>> drivers/mfd/stpmu1.c | 490 ++++++++++++++++++++++++++++++++++++
>> include/dt-bindings/mfd/st,stpmu1.h | 46 ++++
>> include/linux/mfd/stpmu1.h | 220 ++++++++++++++++
>> 5 files changed, 771 insertions(+)
>> create mode 100644 drivers/mfd/stpmu1.c
>> create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
>> create mode 100644 include/linux/mfd/stpmu1.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index b860eb5..e15140b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1812,6 +1812,20 @@ config MFD_STM32_TIMERS
>> for PWM and IIO Timer. This driver allow to share the
>> registers between the others drivers.
>>
>> +config MFD_STPMU1
>> + tristate "Support for STPMU1 PMIC"
>> + depends on (I2C=y && OF)
>> + select REGMAP_I2C
>> + select REGMAP_IRQ
>> + select MFD_CORE
>> + help
>> + Support for ST Microelectronics STPMU1 PMIC. Stpmu1 mfd driver is
>> + the core driver for stpmu1 component that mainly handles interrupts.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called stpmu1.
>> +
>> +
> Extra line not needed.
>
>> menu "Multimedia Capabilities Port drivers"
>> depends on ARCH_SA1100
>>
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index e9fd20d..f1c4be1 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -220,6 +220,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
>> obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
>>
>> obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
>> +obj-$(CONFIG_MFD_STPMU1) += stpmu1.o
>> obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
>>
>> obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
>> diff --git a/drivers/mfd/stpmu1.c b/drivers/mfd/stpmu1.c
>> new file mode 100644
>> index 0000000..a284a3e
>> --- /dev/null
>> +++ b/drivers/mfd/stpmu1.c
>> @@ -0,0 +1,490 @@
>> +// SPDX-License-Identifier: GPL-2.0
> There is a license mismatch between SPDX and MODULE_LICENSE. Or SPDX
> identifier should be GPL-2.0-or-later or MODULE_LICENSE should be
> ("GPL v2")
>
> See https://elixir.bootlin.com/linux/latest/source/include/linux/module.h#L175
>
>> +/*
>> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
>> + * Author: Philippe Peurichard <philippe.peurichard@st.com>,
>> + * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
>> + */
>> +
> I think that Lee, like Linus, prefers the C++ style here
>
>> +#include <linux/err.h>
> That this include is not needed.
>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/mfd/stpmu1.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
> ditto
>> +#include <linux/pm_runtime.h>
> ditto
>
>> +#include <linux/pm_wakeirq.h>
>> +#include <linux/regmap.h>
>> +#include <dt-bindings/mfd/st,stpmu1.h>
>> +
> [snip]
>
>> +
>> +static int stpmu1_configure_from_dt(struct stpmu1_dev *pmic_dev)
>> +{
>> + struct device_node *np = pmic_dev->np;
>> + u32 reg = 0;
> You don't need to initialize reg to 0, anyway will be overwriten.
>
>> + int ret = 0;
> You don't need to initialize ret to 0, anyway will be overwritten.
>
>> + int irq;
>> +
>> + irq = of_irq_get(np, 0);
>> + if (irq <= 0) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to get irq config: %d\n", irq);
> This can be in one line.
>
>> + return irq ? irq : -ENODEV;
> nit: return irq ?: -ENODEV;
>
>> + }
>> + pmic_dev->irq = irq;
>> +
>> + irq = of_irq_get(np, 1);
>> + if (irq <= 0) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to get irq_wake config: %d\n", irq);
>> + return irq ? irq : -ENODEV;
> nit: return irq ?: -ENODEV;
>
>> + }
>> + pmic_dev->irq_wake = irq;
>> +
>> + device_init_wakeup(pmic_dev->dev, true);
>> + ret = dev_pm_set_dedicated_wake_irq(pmic_dev->dev, pmic_dev->irq_wake);
>> + if (ret)
>> + dev_warn(pmic_dev->dev, "failed to set up wakeup irq");
>> +
>> + if (!of_property_read_u32(np, "st,main_control_register", ®)) {
>> + ret = regmap_update_bits(pmic_dev->regmap,
>> + SWOFF_PWRCTRL_CR,
>> + PWRCTRL_POLARITY_HIGH |
>> + PWRCTRL_PIN_VALID |
>> + RESTART_REQUEST_ENABLED,
>> + reg);
>> + if (ret) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to update main control register: %d\n",
>> + ret);
>> + return ret;
>> + }
>> + }
>> +
>> + if (!of_property_read_u32(np, "st,pads_pull_register", ®)) {
>> + ret = regmap_update_bits(pmic_dev->regmap,
>> + PADS_PULL_CR,
>> + WAKEUP_DETECTOR_DISABLED |
>> + PWRCTRL_PD_ACTIVE |
>> + PWRCTRL_PU_ACTIVE |
>> + WAKEUP_PD_ACTIVE,
>> + reg);
>> + if (ret) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to update pads control register: %d\n",
>> + ret);
>> + return ret;
>> + }
>> + }
>> +
>> + if (!of_property_read_u32(np, "st,vin_control_register", ®)) {
>> + ret = regmap_update_bits(pmic_dev->regmap,
>> + VBUS_DET_VIN_CR,
>> + VINLOW_CTRL_REG_MASK,
>> + reg);
>> + if (ret) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to update vin control register: %d\n",
>> + ret);
>> + return ret;
>> + }
>> + }
>> +
>> + if (!of_property_read_u32(np, "st,usb_control_register", ®)) {
>> + ret = regmap_update_bits(pmic_dev->regmap, BST_SW_CR,
>> + BOOST_OVP_DISABLED |
>> + VBUS_OTG_DETECTION_DISABLED |
>> + SW_OUT_DISCHARGE |
>> + VBUS_OTG_DISCHARGE |
>> + OCP_LIMIT_HIGH,
>> + reg);
>> + if (ret) {
>> + dev_err(pmic_dev->dev,
>> + "Failed to update usb control register: %d\n",
>> + ret);
>> + return ret;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int stpmu1_device_init(struct stpmu1_dev *pmic_dev)
>> +{
>> + int ret;
>> + unsigned int val;
>> +
>> + pmic_dev->regmap =
>> + devm_regmap_init_i2c(pmic_dev->i2c, &stpmu1_regmap_config);
>> +
>> + if (IS_ERR(pmic_dev->regmap)) {
>> + ret = PTR_ERR(pmic_dev->regmap);
> You can remove this ...
>
>> + dev_err(pmic_dev->dev, "Failed to allocate register map: %d\n",
>> + ret);
>> + return ret;
> and just return PTR_ERR(pmic_dev->regmap);
>
>> + }
>> +
>> + ret = stpmu1_configure_from_dt(pmic_dev);
>> + if (ret < 0) {
> Is ret >0 return valid? If not, perhaps "if (ret)" would be better.
>
>> + dev_err(pmic_dev->dev,
>> + "Unable to configure PMIC from Device Tree: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + /* Read Version ID */
>> + ret = regmap_read(pmic_dev->regmap, VERSION_SR, &val);
>> + if (ret < 0) {
> Is ret >0 return valid? If not, perhaps "if (ret)" would be better.
>
>> + dev_err(pmic_dev->dev, "Unable to read pmic version\n");
>> + return ret;
>> + }
>> + dev_dbg(pmic_dev->dev, "PMIC Chip Version: 0x%x\n", val);
> nit: Maybe that should be dev_info instead of dev_dbg?
>
>> +
>> + /* Initialize PMIC IRQ Chip & IRQ domains associated */
>> + ret = devm_regmap_add_irq_chip(pmic_dev->dev, pmic_dev->regmap,
>> + pmic_dev->irq,
>> + IRQF_ONESHOT | IRQF_SHARED,
>> + 0, &stpmu1_regmap_irq_chip,
>> + &pmic_dev->irq_data);
>> + if (ret < 0) {
> Is ret >0 return valid? If not, perhaps "if (ret)" would be better.
>
>> + dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
>> + ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stpmu1_dt_match[] = {
>> + {.compatible = "st,stpmu1"},
>> + {},
> I'd rewrite this as
> + { .compatible = "st,stpmu1" },
> + { }
> Space after/before brackets and no comma at the end. The sentinel
> indicates the last item on structure/arrays so no need to add a comma
> at the end.
>
>> +};
>> +
> Remove this line
>
>> +MODULE_DEVICE_TABLE(of, stpmu1_dt_match);
>> +
>> +static int stpmu1_remove(struct i2c_client *i2c)
>> +{
>> + struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
>> +
>> + of_platform_depopulate(pmic_dev->dev);
>> +
>> + return 0;
>> +}
> You can remove this function, see below ...
>
>> +
>> +static int stpmu1_probe(struct i2c_client *i2c,
>> + const struct i2c_device_id *id)
>> +{
>> + struct stpmu1_dev *pmic;
>> + struct device *dev = &i2c->dev;
>> + int ret = 0;
> No need to initialize to 0 if ...
>
>> +
>> + pmic = devm_kzalloc(dev, sizeof(struct stpmu1_dev), GFP_KERNEL);
>> + if (!pmic)
>> + return -ENOMEM;
>> +
>> + pmic->np = dev->of_node;
>> +
>> + dev_set_drvdata(dev, pmic);
>> + pmic->dev = dev;
>> + pmic->i2c = i2c;
>> +
>> + ret = stpmu1_device_init(pmic);
>> + if (ret < 0)
> Is ret >0 return valid? If not, perhaps "if (ret)" would be better.
>> + goto err;
> return ret;
>
>> +
>> + ret = of_platform_populate(pmic->np, NULL, NULL, pmic->dev);
>> +
> ret = devm_of_platform_populate(pmic->dev);
>
> or even better
>
> return devm_of_platform_populate(pmic->dev);
>
> And remove the stpmu1_remove function.
From the regulator driver review, Mark Brown suggest to use
mfd_add_devices() to remove the compatible strings in the child drivers.
This means adding struct mfd_cell with a list of devices to probe.
Is it ok if I switch to mfd_add_devices() ?
>> + dev_dbg(dev, "stpmu1 driver probed\n");
> That message looks redundant to me. I'd remove it.
>
>> +err:
> And you can remove this label.
>
>> + return ret;
> And this
>
>> +}
>> +
>> +static const struct i2c_device_id stpmu1_id[] = {
>> + {"stpmu1", 0},
>> + {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(i2c, stpmu1_id);
> The above code shouldn't be needed anymore for DT-only devices. See
> da10c06a044b ("i2c: Make I2C ID tables non-mandatory for DT'ed
> devices")
>
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stpmu1_suspend(struct device *dev)
>> +{
>> + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
>> + struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
>> +
>> + if (device_may_wakeup(dev))
>> + enable_irq_wake(pmic_dev->irq_wake);
>> +
>> + disable_irq(pmic_dev->irq);
>> + return 0;
>> +}
>> +
>> +static int stpmu1_resume(struct device *dev)
>> +{
>> + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
>> + struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
>> +
>> + regcache_sync(pmic_dev->regmap);
> Maybe you would like to check for an error here.
>
>> +
>> + if (device_may_wakeup(dev))
>> + disable_irq_wake(pmic_dev->irq_wake);
>> +
>> + enable_irq(pmic_dev->irq);
>> + return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(stpmu1_pm, stpmu1_suspend, stpmu1_resume);
>> +
>> +static struct i2c_driver stpmu1_driver = {
>> + .driver = {
>> + .name = "stpmu1",
>> + .owner = THIS_MODULE,
> This is not needed, the core does it for you.
>
>> + .pm = &stpmu1_pm,
>> + .of_match_table = of_match_ptr(stpmu1_dt_match),
> It is a DT-only device so no need the of_match_ptr.
>
>> + },
>> + .probe = stpmu1_probe,
>> + .remove = stpmu1_remove,
> Now you can remove this
>
>> + .id_table = stpmu1_id,
> And you can remove this also.
>
>> +};
>> +
>> +module_i2c_driver(stpmu1_driver);
>> +
>> +MODULE_DESCRIPTION("STPMU1 PMIC I2C Client");
> nit: PMIC I2C Client sounds weird to me, "STPMU1 PMIC driver" ? Note
> that I am not english native so I could be wrong.
>
>> +MODULE_AUTHOR("<philippe.peurichard@st.com>");
> Use "Name <email>" or just "Name"
>
>> +MODULE_LICENSE("GPL");
> As I told you there is a license mismatch with SPDX.
>
> [snip]
>
> Best regards,
> Enric
>
Best Regards,
pascal
^ permalink raw reply
* [PATCH] HID: i2c-hid: Fix flooded incomplete report after S3 on Rayd touchscreen
From: AceLan Kao @ 2018-08-21 8:55 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Hans de Goede, Dmitry Torokhov,
Aaron Ma, linux-input, linux-kernel
The incomplete report flooded after S3 and touchscreen becomes
malfunctioned.
[ 1367.646244] i2c_hid i2c-CUST0000:00: i2c_hid_get_input: incomplete report (58/18785)
[ 1367.649471] i2c_hid i2c-CUST0000:00: i2c_hid_get_input: incomplete report (58/28743)
[ 1367.651092] i2c_hid i2c-CUST0000:00: i2c_hid_get_input: incomplete report (58/26757)
[ 1367.652658] i2c_hid i2c-CUST0000:00: i2c_hid_get_input: incomplete report (58/52280)
[ 1367.654287] i2c_hid i2c-CUST0000:00: i2c_hid_get_input: incomplete report (58/56059)
Adding device ID, 04F3:30CC, to the quirk to re-send report description
after resume.
Cc: stable@vger.kernel.org
Signed-off-by: AceLan Kao <acelan.kao@canonical.com>
---
drivers/hid/hid-ids.h | 1 +
drivers/hid/i2c-hid/i2c-hid.c | 2 ++
2 files changed, 3 insertions(+)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index c7981ddd8776..e80bcd71fe1e 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -528,6 +528,7 @@
#define I2C_VENDOR_ID_RAYD 0x2386
#define I2C_PRODUCT_ID_RAYD_3118 0x3118
+#define I2C_PRODUCT_ID_RAYD_4B33 0x4B33
#define USB_VENDOR_ID_HANWANG 0x0b57
#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index eae0cb3ddec6..5fd1159fc095 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -174,6 +174,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_RESEND_REPORT_DESCR },
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS10FB_TOUCH,
I2C_HID_QUIRK_RESEND_REPORT_DESCR },
+ { I2C_VENDOR_ID_RAYD, I2C_PRODUCT_ID_RAYD_4B33,
+ I2C_HID_QUIRK_RESEND_REPORT_DESCR },
{ 0, 0 }
};
--
2.17.1
^ permalink raw reply related
* Re: mfd: cros_ec: Check for mkbp events on resume only if supported.
From: Marek Szyprowski @ 2018-08-21 7:28 UTC (permalink / raw)
To: RaviChandra Sadineni, lee.jones, dmitry.torokhov, ravisadineni,
dtor, linux-samsung-soc, tbroch, linux-kernel, linux-input, tfiga
In-Reply-To: <20180820153419.32098-1-ravisadineni@google.com>
Hi Ravi,
On 2018-08-20 17:34, RaviChandra Sadineni wrote:
> Currently on every resume we check for mkbp events and notify the
> clients. This helps in identifying the wakeup sources. But on devices
> that do not support mkbp protocol, we might end up querying key state of
> the keyboard in a loop which blocks the resume. Instead check for events
> only if mkbp is supported.
>
> Signed-off-by: RaviChandra Sadineni <ravisadineni@chromium.org>
Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
> Note: This patch fixes the suspend/resume issue on Snow and Peach-Pit
> Chromebooks, both on vanilla v4.18 as well as linux-next from 20 August
> 2018. Further info at: https://lkml.org/lkml/2018/6/5/1076
>
> drivers/mfd/cros_ec.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
> index 65a9757a6d21..fe6f83766144 100644
> --- a/drivers/mfd/cros_ec.c
> +++ b/drivers/mfd/cros_ec.c
> @@ -218,7 +218,8 @@ EXPORT_SYMBOL(cros_ec_suspend);
>
> static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
> {
> - while (cros_ec_get_next_event(ec_dev, NULL) > 0)
> + while (ec_dev->mkbp_event_supported &&
> + cros_ec_get_next_event(ec_dev, NULL) > 0)
> blocking_notifier_call_chain(&ec_dev->event_notifier,
> 1, ec_dev);
> }
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* [PATCH V4 3/3] Input: new da7280 haptic driver
From: Roy Im @ 2018-08-21 5:44 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring
Cc: Mark Rutland, Support Opensource, devicetree, linux-input,
linux-kernel
In-Reply-To: <cover.1534830272.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>
---
v4: Updated code as dt-bindings are changed.
v3: No changes.
v2: Fixed kbuild error/warning
drivers/input/misc/Kconfig | 12 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/da7280.c | 1438 +++++++++++++++++++++++++++++++++++++++++++
drivers/input/misc/da7280.h | 412 +++++++++++++
4 files changed, 1863 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..6e0de69 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -851,4 +851,16 @@ 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 && SYSFS) || PWM
+ select INPUT_FF_MEMLESS
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the haptics controller on
+ Dialog DA7280 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da7280-haptic.
+
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..da500ab
--- /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-MODE"))
+ 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,vib-mode", &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 skip to initialze and 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 PATCH V4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox