Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v5 7/8] dt-bindings: watchdog: document stpmic1 pmic watchdog
From: Guenter Roeck @ 2018-10-30 13:17 UTC (permalink / raw)
  To: Pascal PAILLET-LME, dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
In-Reply-To: <1540830122-2577-8-git-send-email-p.paillet@st.com>

On 10/29/18 9:22 AM, Pascal PAILLET-LME wrote:
> The stpmic1 PMIC embeds a watchdog which is disabled by default.
> In case of watchdog, the PMIC goes off.
> 
> Signed-off-by: Pascal Paillet <p.paillet@st.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
> changes in v5: nothing
> 
>   Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt | 11 +++++++++++
>   1 file changed, 11 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
> new file mode 100644
> index 0000000..7cc1407
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
> @@ -0,0 +1,11 @@
> +STMicroelectronics STPMIC1 Watchdog
> +
> +Required properties:
> +
> +- compatible : should be "st,stpmic1-wdt"
> +
> +Example:
> +
> +watchdog {
> +	compatible = "st,stpmic1-wdt";
> +};
> 

^ permalink raw reply

* Re: [PATCH v5 8/8] watchdog: stpmic1: add stpmic1 watchdog driver
From: Guenter Roeck @ 2018-10-30 13:17 UTC (permalink / raw)
  To: Pascal PAILLET-LME, dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
In-Reply-To: <1540830122-2577-9-git-send-email-p.paillet@st.com>

On 10/29/18 9:22 AM, Pascal PAILLET-LME wrote:
> The stpmic1 PMIC embeds a watchdog which is disabled by default. As soon
> as the watchdog is started, it must be refreshed periodically otherwise
> the PMIC goes off.
> 
> Signed-off-by: Pascal Paillet <p.paillet@st.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
> changes in v5:
> * initialize wdtdev.parent
> * fix checkpatch warning
> 
>   drivers/watchdog/Kconfig       |  12 ++++
>   drivers/watchdog/Makefile      |   1 +
>   drivers/watchdog/stpmic1_wdt.c | 139 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 152 insertions(+)
>   create mode 100644 drivers/watchdog/stpmic1_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 5ea8909..6d2ffef 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -806,6 +806,18 @@ config STM32_WATCHDOG
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called stm32_iwdg.
>   
> +config STPMIC1_WATCHDOG
> +	tristate "STPMIC1 PMIC watchdog support"
> +	depends on MFD_STPMIC1
> +	select WATCHDOG_CORE
> +	help
> +	  Say Y here to include watchdog support embedded into STPMIC1 PMIC.
> +	  If the watchdog timer expires, stpmic1 will shut down all its power
> +	  supplies.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called spmic1_wdt.
> +
>   config UNIPHIER_WATCHDOG
>   	tristate "UniPhier watchdog support"
>   	depends on ARCH_UNIPHIER || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index bf92e7b..2649cf3 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -217,3 +217,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
>   obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
>   obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
>   obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
> +obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
> diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
> new file mode 100644
> index 0000000..ad431d8
> --- /dev/null
> +++ b/drivers/watchdog/stpmic1_wdt.c
> @@ -0,0 +1,139 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) STMicroelectronics 2018
> +// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
> +
> +#include <linux/kernel.h>
> +#include <linux/mfd/stpmic1.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/watchdog.h>
> +
> +/* WATCHDOG CONTROL REGISTER bit */
> +#define WDT_START		BIT(0)
> +#define WDT_PING		BIT(1)
> +#define WDT_START_MASK		BIT(0)
> +#define WDT_PING_MASK		BIT(1)
> +#define WDT_STOP		0
> +
> +#define PMIC_WDT_MIN_TIMEOUT 1
> +#define PMIC_WDT_MAX_TIMEOUT 256
> +#define PMIC_WDT_DEFAULT_TIMEOUT 30
> +
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +module_param(nowayout, bool, 0);
> +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
> +		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +
> +struct stpmic1_wdt {
> +	struct stpmic1 *pmic;
> +	struct watchdog_device wdtdev;
> +};
> +
> +static int pmic_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	return regmap_update_bits(wdt->pmic->regmap,
> +				  WCHDG_CR, WDT_START_MASK, WDT_START);
> +}
> +
> +static int pmic_wdt_stop(struct watchdog_device *wdd)
> +{
> +	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	return regmap_update_bits(wdt->pmic->regmap,
> +				  WCHDG_CR, WDT_START_MASK, WDT_STOP);
> +}
> +
> +static int pmic_wdt_ping(struct watchdog_device *wdd)
> +{
> +	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	return regmap_update_bits(wdt->pmic->regmap,
> +				  WCHDG_CR, WDT_PING_MASK, WDT_PING);
> +}
> +
> +static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
> +				unsigned int timeout)
> +{
> +	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	/* timeout is equal to register value + 1 */
> +	return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
> +}
> +
> +static const struct watchdog_info pmic_watchdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "STPMIC1 PMIC Watchdog",
> +};
> +
> +static const struct watchdog_ops pmic_watchdog_ops = {
> +	.owner = THIS_MODULE,
> +	.start = pmic_wdt_start,
> +	.stop = pmic_wdt_stop,
> +	.ping = pmic_wdt_ping,
> +	.set_timeout = pmic_wdt_set_timeout,
> +};
> +
> +static int pmic_wdt_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct stpmic1 *pmic;
> +	struct stpmic1_wdt *wdt;
> +
> +	if (!pdev->dev.parent)
> +		return -EINVAL;
> +
> +	pmic = dev_get_drvdata(pdev->dev.parent);
> +	if (!pmic)
> +		return -EINVAL;
> +
> +	wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
> +	if (!wdt)
> +		return -ENOMEM;
> +
> +	wdt->pmic = pmic;
> +
> +	wdt->wdtdev.info = &pmic_watchdog_info;
> +	wdt->wdtdev.ops = &pmic_watchdog_ops;
> +	wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
> +	wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
> +	wdt->wdtdev.parent = &pdev->dev;
> +
> +	wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(&wdt->wdtdev, nowayout);
> +	watchdog_set_drvdata(&wdt->wdtdev, wdt);
> +
> +	ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
> +	return 0;
> +}
> +
> +static const struct of_device_id of_pmic_wdt_match[] = {
> +	{ .compatible = "st,stpmic1-wdt" },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
> +
> +static struct platform_driver stpmic1_wdt_driver = {
> +	.probe = pmic_wdt_probe,
> +	.driver = {
> +		.name = "stpmic1-wdt",
> +		.of_match_table = of_pmic_wdt_match,
> +	},
> +};
> +module_platform_driver(stpmic1_wdt_driver);
> +
> +MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
> +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply

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

[-- Attachment #1: Type: text/plain, Size: 1282 bytes --]

Hi!

> > https://github.com/hackerspace/olpc-xo175-linux/wiki/How-to-run-an-up-to-date-Linux-on-a-XO-1.75
> > 
> > I didn't test it yet -- will do when I get home in the evening. But
> > chances are it's good enough and I guess you'd be able to get it
> > working even if I messed up some details.

Instruction say make, but you really need to do make zImage and make
mmp2-olpc-xo-1-75.dtb .

Instructions say "make -j", which is nice way to crash a machine. I
suggest "make -j 8" :-).

Ok, I think I followed the instructions. I am using empty root
filesystem, just with /boot -- want to see kernel messages first
before I mess with the userland.

But I don't get kernel messages during boot:

OLPC 1B1, 512 MiB memory...
OpenFirmware Q4C02 EC Frimware 0.3.02

Type any key to interrupt...
(but keys do not interrupt anything, ouch).
Warning... partition type is 0xb...
Boot device: /usb/disk:\boot\olpc.fth Arguments:
Boot device ....
Warning... partition type is 0xb...

And that's it, cursor and no kernel messages.

Any ideas?

Could I get prepared binary zImage for testing?

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply

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

[-- Attachment #1: Type: text/plain, Size: 1831 bytes --]

Hi!

> > > This makes keyboard/touchpad work on a DT MMP2 platform.
> > > 
> > > I believe that it would be a good idea if this, once reviewed, went
> > > in
> > > via the input tree. The DT and CLK parts got reviews/acks.
> > > 
> > > Changes from v1:
> > > - Basically none, just re-send, including Ack and Review tags in
> > > patches
> > >   that received them. Plus an extra Cc.
> > >   I'd be very thankful for reviews.
> > 
> > Ok, I believe I have OLPC 1.75, and it is actually a nice
> > machine. Can I get recent kernel working there in useful fashion? (Is
> > there docs somewhere)?
> 
> A lovely machine indeed. I've written this now:
> 
> https://github.com/hackerspace/olpc-xo175-linux/wiki/How-to-run-an-up-to-date-Linux-on-a-XO-1.75
> 
> I didn't test it yet -- will do when I get home in the evening. But
> chances are it's good enough and I guess you'd be able to get it
> working even if I messed up some details.

Ok, let me check. I went to this branch:

commit f99b274084d320814f4b035a7e3455af424229ea
Author: Lubomir Rintel <lkundrak@v3.sk>
Date:   Sun Aug 5 23:11:27 2018 +0200

First comment: the zImage size adjusting hack should really be
somewhere in tools/ and preferably in python. This is way beyond
reasonable use of shell.

> Please let me know if it works for you -- I'm having trouble getting
> reviews/acks/tested-bys for the XO-1.75 patch sets [1] and will be
> super thankful if I get some feedback from you.
> 
> [1] https://lore.kernel.org/patchwork/bundle/lkundrak/OLPC%20XO%201.75/

You want to just continue without them :-). As long as you don't
change the core code, you should be ok.

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Peter Hutterer @ 2018-10-30  6:26 UTC (permalink / raw)
  To: Harry Cutts
  Cc: torvalds, jikos, benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <CA+jURcvAQWP-fMLxNBF=PZ7moJZgorjO0SGXn8xSWKt=k+oe1w@mail.gmail.com>

On Mon, Oct 29, 2018 at 04:03:54PM -0700, Harry Cutts wrote:
> On Mon, 29 Oct 2018 at 15:01, Linus Torvalds
> <torvalds@linux-foundation.org> wrote:
> > That would work, yes.
> 
> OK, I'll write a patch for this. (It may be next week, though, as I
> have a deadline on a separate project this week.)
> 
> > Except I think you *do* want the "reset on direction change" logic,
> > because otherwise we still end up having the:
> >
> > > - we update remainder to -1
> >
> > where it now gets easier to next time go the wrong way, for no good
> > reason.  So now you only need another 6/8ths the other way to get to
> > within 7/8ths of -8 and scroll back.
> >
> > In other words, the whole "round partial scrolling" also causes that
> > whole "now the other direction is closer" issue.
> >
> > At 7/8's it is less obviously a problem than it was at 1/2, but I
> > still think it's a sign of an unstable algorithm, where changes get
> > triggered too easily in the non-highres world.
> >
> > Also, honestly, I'm not sure I see the point. *IF* you actually scroll
> > more in one direction, it doesn't matter one whit whether you pick
> > 1/2, 7/8, or whole multipliers: the *next* step is still always going
> > to be one whole multiplier away.
> >
> > So I think the whole rounding is actually misguided. I think it may
> > come from the very fact that you did *not* reset the remainder on
> > direction changes, so you could scroll in one direction to -3, and
> > then you change direction and go a "whole" tick the other way, but now
> > it's just at +5, so you think you need to round up.
> >
> > With the whole "reset when changing direction", I don't think the
> > rounding is necessary, and I don't think it makes sense.
> 
> Resetting on direction change would certainly make complete sense in
> smooth mode. The reason that I'm reluctant to do it is for clicky
> mode, where we think it's important that the low-res event happen at a
> consistent point in the movement between notches (the resting
> positions of the wheel). For example, imagine the following scenario
> with a wheel multiplier of 8 and the threshold initially at 7/8ths of
> a notch:
> 
> - I scroll one notch down. The low-res event occurs just before the
> wheel settles in to its notch, leaving a -1/8th remainder, and then
> (on most wheels) the ratchet mechanism settles the wheel 1/8th further
> into its resting position, eliminating the remainder.
> - I move the wheel 3/8ths further down, then change my mind and start
> scrolling upwards.
> 
> If we reset on direction change at this point, then the "zero point"
> will have moved, so that we trigger the low-res movement at -4/8ths
> (at the peak of resistance between the two notches) instead of at
> 7/8ths. If we don't reset but allow the 3/8ths remainder to be
> cleared, the trigger point stays at 7/8ths. It's a minor thing, to be
> sure, but we think that keeping the on-screen response consistent with
> the tactile feel of the wheel is important for the user experience.

IMO this is a lost battle because you cannot know when the ratchet is
enabled or not (at least not on all mice). Users switch between ratchet and
freewheeling time and once you're out of one mode, you have no reference 
to the other mode's reset point anymore.

you could guess it with heuristics. if you get multiple scroll sequences
with $multiplier events, then you're probably back in ratchet mode. Of
course, it's just guesswork...

fwiw, here's a writeup of the issues that I found in the current code,
before Linus' patch. This is as much my note-taking as it is an email.

Let's assume free-wheeling for now, and I'm using high-res values of
2 to reduce typing. multiplier is 8 like the default in the code.

- the first event comes earlier than the second on a consistent scroll
  motion, you get one event after a half movement, the second, third, ...
  events after n + half. Not a huge issue since it only ever happens once
  after plug.  And this is by design as you said, so let's live with
  that :)
- The scroll wheel emulation is unpredictable across scroll events. Let's
  assume multiple sequences of events, with a pause long enough to make the
  user think they are independent scroll motions:
    [2, 2, 2, 2] [2, 2, 2, 2] ← input events
        x            x
    [2, 2] [2, 2, 2, 2, 2, 2] ← input events
        x            x
    [2, 2, 2, 2, 2] [2, 2, 2] ← input events
        x            x
  x marks the spot where the low-res event is sent.
  in the first case, everything is fine, second case has the first sequence
  react quickly, the second one slower. third case is the opposite. The only
  reason this isn't very obvious is because the scroll distance is very
  small either way. we'd need a timeout to avoid this issue, a basic "reset
  remainder after N ms".
- the directional change is what Linus triggered
              [2, 2, -2, 2, -2 ...] ← input events
  remainders:  0  4              r - 8
                 -4  -6          r + 8
                      2  4       r - 8
                        -4  -6
                  x   x  x   x
  if you get in the right state you get to trigger the events on every small
  motion. Note that the issue here isn't the half-way trigger, but the
  missing reset. we could have the half-way trigger in place:
  [2, 2, -2, 2, -2 ...] ← input events
   0  4
     -4   0 ← reset 
         -2  0 ← reset 
             2   0 ← reset 
      x          
  so that would work just fine, that's also what the patch below does.
- fast motion can trigger some odd calculations because of the +1 added.
    [2, 8, -2, 2 ...] ← input events
  r: 0 10               r - 16 
       -6  -8           r + 16 (incorrect)
            8 10        r - 16 (incorrect)
              -6  
       xx  xx xx
  All of these would trigger a double scroll event. Admittedly this is
  physically hard to trigger so it's more a theoretical concern. What's
  easier to trigger is:
    [2, 6, 2, 2 ...] ← input events
     0  8               r - 16
       -8 -6            r + 8 (incorrect)
           2 4          r - 8
            -4  
       xx  x x
  The xx and y events are incorrect here, total movement was 10 units but we
  got 3 units instead of the expected 1. Result is that on fast scrolling we
  may end up with the occasional extra event. 

  The fix for this is in the patch below, by only adding the extra ±1 if
  we're below the multiplier, we get this instead:
    [2, 6, 2, 2 ...] ← input events
     0  8               r - 8
        0  2  4         r + 8
             -4         r - 8
       xx     x

  or on a similar sequence:
    [2, 8, 6, 2 ...] ← input events
     0  8               r - 8
        0  6  4         r + 8
          -2 -4         r - 8
       xx  x  x


Other issues I found with an MX Anywhere 2S is that on slow scroll and in
ratchet mode we get some scroll jitter.  In ratchet mode  we can get this
sequence if you scroll just past the notch and it snaps back:
    [1, 1, 1, 1, 1, 1, 1, 1, -1]
That's quite easy to trigger. In free-wheel mode we may get the same for
slow motion due to human finger jitter (the Anywhere 2S didn't have HW
jitter, but other devices may). So a perceived-consistent scroll motion may
really look like this:
    [1, 1, 1, 1, 1, -1, 1, 1]
Hard to triggger but when it does, it feels like we're dropping events.
The former isn't that much of an issue as long as the ratchet is enabled so
you get the haptic feedback and we (usually) don't drop events.

Finally: I reproduced the issue you mentioned with the 7/8th of a
notch. A slow ratchet scroll does not always give me the same number of
events per notch, there are cases where I get 7 or 9 events instead of the
expected 8 (all with a hi-res value 1). With Linus patch I ended up getting
weirdly ouf of sync here as to when the low res event was triggered during
the motion.

So my suggestion is to combine Linus' reset with your approach and use the
center-point for the trigger. This gives us a few events to slide and still
do the right thing, and any direction change will reset anyway. Biggest
drawback is that the first event after a direction change is triggered
faster than the next event. Otherwise it feels correct to me, both in
free-wheeling and in ratchet mode now.

The timeout I mentioned above is probably something better kept in userspace
if needed.

Cheers,
   Peter

Also, WTF moment: I managed to get the mouse into a state where it would
only give me 1 hi-res event per notch movement but failed to reproduce that
again.


>From a9cb724159cc9515ce9ee1aff15184a19731d80b Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Tue, 30 Oct 2018 14:10:53 +1000
Subject: [PATCH] HID: input: restore the hi-res scroll emulation to work on
 the midpoint

A single notch movement does not always cause exactly $multiplier events in
the hardware. Setting the trigger at the midpoint allows us to slide a few
events either direction while still triggering exactly one scroll event per
multiplier.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
---
 drivers/hid/hid-input.c | 25 ++++++++++++++++++-------
 include/linux/hid.h     |  1 +
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index a2f74e6adc70..a170a6823072 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1855,7 +1855,7 @@ EXPORT_SYMBOL_GPL(hidinput_disconnect);
 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
 				      int hi_res_value)
 {
-	int low_res_value, remainder, multiplier;
+	int low_res_value, remainder, multiplier, direction;
 
 	input_report_rel(counter->dev, REL_WHEEL_HI_RES,
 			 hi_res_value * counter->microns_per_hi_res_unit);
@@ -1865,20 +1865,31 @@ void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
 	 * but reset if the direction has changed.
 	 */
 	remainder = counter->remainder;
-	if ((remainder ^ hi_res_value) < 0)
+	direction = hi_res_value > 0 ? 1 : -1;
+	if (direction != counter->direction)
 		remainder = 0;
+	counter->direction = direction;
 	remainder += hi_res_value;
 
 	/*
 	 * Then just use the resolution multiplier to see if
 	 * we should send a low-res (aka regular wheel) event.
+	 * Threshold is at the mid-point because we'll slide a few events
+	 * back/forth when the mouse gives us more or less than multiplier
+	 * events for a single notch movement.
 	 */
 	multiplier = counter->resolution_multiplier;
-	low_res_value = remainder / multiplier;
-	remainder -= low_res_value * multiplier;
-	counter->remainder = remainder;
-
-	if (low_res_value)
+	if (abs(remainder) >= multiplier/2) {
+		low_res_value = remainder / multiplier;
+                /* Move at minimum 1/-1 because we want to trigger when the wheel
+                 * is half-way to the next notch (i.e. scroll 1 notch after a
+                 * 1/2 notch movement).
+                 */
+		if (low_res_value == 0)
+			low_res_value = (hi_res_value > 0 ? 1 : -1);
+		remainder -= low_res_value * multiplier;
 		input_report_rel(counter->dev, REL_WHEEL, low_res_value);
+	}
+	counter->remainder = remainder;
 }
 EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 2827b87590d8..9752b8a2ee42 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1162,6 +1162,7 @@ struct hid_scroll_counter {
 	int resolution_multiplier;
 
 	int remainder;
+	int direction;
 };
 
 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
-- 
2.19.1

^ permalink raw reply related

* Re: Logitech high-resolution scrolling..
From: Harry Cutts @ 2018-10-29 23:03 UTC (permalink / raw)
  To: torvalds
  Cc: jikos, benjamin.tissoires, linux-input, linux-kernel,
	Peter Hutterer
In-Reply-To: <CAHk-=wihpgjONY2iw5mO9gHcQHxRyj+aQcaX2qd9Tjf-MdGNZg@mail.gmail.com>

On Mon, 29 Oct 2018 at 15:01, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
> That would work, yes.

OK, I'll write a patch for this. (It may be next week, though, as I
have a deadline on a separate project this week.)

> Except I think you *do* want the "reset on direction change" logic,
> because otherwise we still end up having the:
>
> > - we update remainder to -1
>
> where it now gets easier to next time go the wrong way, for no good
> reason.  So now you only need another 6/8ths the other way to get to
> within 7/8ths of -8 and scroll back.
>
> In other words, the whole "round partial scrolling" also causes that
> whole "now the other direction is closer" issue.
>
> At 7/8's it is less obviously a problem than it was at 1/2, but I
> still think it's a sign of an unstable algorithm, where changes get
> triggered too easily in the non-highres world.
>
> Also, honestly, I'm not sure I see the point. *IF* you actually scroll
> more in one direction, it doesn't matter one whit whether you pick
> 1/2, 7/8, or whole multipliers: the *next* step is still always going
> to be one whole multiplier away.
>
> So I think the whole rounding is actually misguided. I think it may
> come from the very fact that you did *not* reset the remainder on
> direction changes, so you could scroll in one direction to -3, and
> then you change direction and go a "whole" tick the other way, but now
> it's just at +5, so you think you need to round up.
>
> With the whole "reset when changing direction", I don't think the
> rounding is necessary, and I don't think it makes sense.

Resetting on direction change would certainly make complete sense in
smooth mode. The reason that I'm reluctant to do it is for clicky
mode, where we think it's important that the low-res event happen at a
consistent point in the movement between notches (the resting
positions of the wheel). For example, imagine the following scenario
with a wheel multiplier of 8 and the threshold initially at 7/8ths of
a notch:

- I scroll one notch down. The low-res event occurs just before the
wheel settles in to its notch, leaving a -1/8th remainder, and then
(on most wheels) the ratchet mechanism settles the wheel 1/8th further
into its resting position, eliminating the remainder.
- I move the wheel 3/8ths further down, then change my mind and start
scrolling upwards.

If we reset on direction change at this point, then the "zero point"
will have moved, so that we trigger the low-res movement at -4/8ths
(at the peak of resistance between the two notches) instead of at
7/8ths. If we don't reset but allow the 3/8ths remainder to be
cleared, the trigger point stays at 7/8ths. It's a minor thing, to be
sure, but we think that keeping the on-screen response consistent with
the tactile feel of the wheel is important for the user experience.

Harry Cutts
Chrome OS Touch/Input team

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Linus Torvalds @ 2018-10-29 22:00 UTC (permalink / raw)
  To: Harry Cutts
  Cc: Jiri Kosina, Benjamin Tissoires, linux-input,
	Linux Kernel Mailing List, Peter Hutterer
In-Reply-To: <CA+jURcvVAJbwazWBByh=GTEV-wC75mZA0oaiX0x4_89F_byM6A@mail.gmail.com>

On Mon, Oct 29, 2018 at 2:42 PM Harry Cutts <hcutts@chromium.org> wrote:
>
> Ah, I see what you mean. So, if we move the threshold to (multiplier -
> 1)/multiplier (7/8) in this case, I think the equivalent scenario
> would be:

That would work, yes.

Except I think you *do* want the "reset on direction change" logic,
because otherwise we still end up having the:

> - we update remainder to -1

where it now gets easier to next time go the wrong way, for no good
reason.  So now you only need another 6/8ths the other way to get to
within 7/8ths of -8 and scroll back.

In other words, the whole "round partial scrolling" also causes that
whole "now the other direction is closer" issue.

At 7/8's it is less obviously a problem than it was at 1/2, but I
still think it's a sign of an unstable algorithm, where changes get
triggered too easily in the non-highres world.

Also, honestly, I'm not sure I see the point. *IF* you actually scroll
more in one direction, it doesn't matter one whit whether you pick
1/2, 7/8, or whole multipliers: the *next* step is still always going
to be one whole multiplier away.

So I think the whole rounding is actually misguided. I think it may
come from the very fact that you did *not* reset the remainder on
direction changes, so you could scroll in one direction to -3, and
then you change direction and go a "whole" tick the other way, but now
it's just at +5, so you think you need to round up.

With the whole "reset when changing direction", I don't think the
rounding is necessary, and I don't think it makes sense.

But I'm willing to test patches. I would suggest looking at the "oops,
direction changed" issue, though, because it really was very annoying.

> I tested these changes with 5 different Logitech mice (see the
> Logitech high-res support patch [0] for details), and did so mainly
> with applications that were *not* high-res aware, using a mix of
> clicky and smooth modes. Admittedly the MX Anywhere 2S was not one of
> my test devices; I had assumed that its behaviour would be
> sufficiently similar to that of the MX Anywhere 2 and the MX Master
> 2S.

I happen to have a MX Master 2S too, but I don't use it because I find
I like the smaller and lightweight "anywhere" mice.

I didn't try the broken case with it, but one thing I notice with the
Master 2S is that it seems to have a "heftier" feel to its wheel. It
may simply have more mass and not be as flighty, and thus show the
issue less.

But that's just a theory. It could just be something that is
individual to some mice.

                       Linus

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Harry Cutts @ 2018-10-29 21:42 UTC (permalink / raw)
  To: torvalds
  Cc: jikos, benjamin.tissoires, linux-input, linux-kernel,
	Peter Hutterer
In-Reply-To: <CAHk-=wh3VO9hD9hUBS-H2d4jjGuYuVTudshHsPe9p-5sYhFfGg@mail.gmail.com>

On Mon, 29 Oct 2018 at 14:12, Linus Torvalds <torvalds@linux-foundation.org>
> So what the half-multiplier did, assuming a multiplier of 8 (which is
> what my MX Anywhere 2S reports) would be:
>
>  - remainder starts at 3
>  - high-res is +1
>  - now remainder is 3+1, and it triggers the >= half logic
>  - 4/8 is 0, but then the code added 1 because high-res was positive,
> so the code decides to add 1
>  - the code does a wheel update of 1, and updates remainder with -8,
> so now it's -4
>
> Next time around, if the high-res update is 0 or -1, it will go the
> other direction. And then it will oscillate.
>
> Notice how tiny movements of +1/-1 in the *high-res* count can
> translate into +1/-1 in the regular wheel movement.

Ah, I see what you mean. So, if we move the threshold to (multiplier -
1)/multiplier (7/8) in this case, I think the equivalent scenario
would be:

- remainder starts at 7
- high-res is +1
- remainder is now 7+1, triggering a low-res update
- 7/8 is 0, but we add one to the remainder in the check making it (7+1)/8 == 1
- we update remainder to -1

This way we're still at least 7/8ths of a notch from the threshold in
either direction, so we shouldn't get the oscillation problem. Does
that sound reasonable, or do you think I've missed something?

> I do not believe that you actually ever *used* that code, or if you
> did, you only did so with applications that were high-res aware and
> ignored the regular wheel entirely because you were testing in an
> environment with other changes than just the kernel.

I tested these changes with 5 different Logitech mice (see the
Logitech high-res support patch [0] for details), and did so mainly
with applications that were *not* high-res aware, using a mix of
clicky and smooth modes. Admittedly the MX Anywhere 2S was not one of
my test devices; I had assumed that its behaviour would be
sufficiently similar to that of the MX Anywhere 2 and the MX Master
2S.

Harry Cutts
Chrome OS Touch/Input team

[0]: https://patchwork.kernel.org/patch/10582935/

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Linus Torvalds @ 2018-10-29 21:11 UTC (permalink / raw)
  To: Harry Cutts
  Cc: Jiri Kosina, Benjamin Tissoires, linux-input,
	Linux Kernel Mailing List, peter.hutterer
In-Reply-To: <CA+jURcttJs05DFCuKCyOfe3oA9Q_KoiP3+g8-OVu4OPm9ufp6Q@mail.gmail.com>

On Mon, Oct 29, 2018 at 12:17 PM Harry Cutts <hcutts@chromium.org> wrote:
>
> I don't really understand why the half-multiplier thing would cause
> the instability; there's still a low-res threshold every 8 high-res
> units whichever way you do it

No there isn't.

So what the half-multiplier did, assuming a multiplier of 8 (which is
what my MX Anywhere 2S reports) would be:

 - remainder starts at 3
 - high-res is +1
 - now remainder is 3+1, and it triggers the >= half logic
 - 4/8 is 0, but then the code added 1 because high-res was positive,
so the code decides to add 1
 - the code does a wheel update of 1, and updates remainder with -8,
so now it's -4

Next time around, if the high-res update is 0 or -1, it will go the
other direction. And then it will oscillate.

Notice how tiny movements of +1/-1 in the *high-res* count can
translate into +1/-1 in the regular wheel movement.

And those tiny movements very definitely happen. Maybe it's just my
mouse, but the undeniable fact is that the old algorithm was simply
not stable.

It was literally unusable. I had to be careful not to even touch the
wheel at all, or it would scroll randomly.

I do not believe that you actually ever *used* that code, or if you
did, you only did so with applications that were high-res aware and
ignored the regular wheel entirely because you were testing in an
environment with other changes than just the kernel.

                    Linus

^ permalink raw reply

* Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation
From: Vladis Dronov @ 2018-10-29 20:51 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <nycvar.YFH.7.76.1810261724510.23511@cbobk.fhfr.pm>

Hello, Jiri,

Thank you for the reply and your opinion. It appeared that my own implementation
of a ring buffer was kind of "inventing a wheel", as "kfifo" is already is the
kernel and it may work as a ring buffer quite well. I would like to rewrite my
patchset and use kfifo instead in a new one. Please, ignore this my patchset and
I'll try to submit v2 soon.

This also will answer to "how was it tested" concern, as I believe, kfifo was
quite tested.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

----- Original Message -----
> From: "Jiri Kosina" <jikos@kernel.org>
> To: "Vladis Dronov" <vdronov@redhat.com>
> Cc: "Benjamin Tissoires" <benjamin.tissoires@redhat.com>, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
> Sent: Friday, October 26, 2018 5:25:21 PM
> Subject: Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation
> 
> On Wed, 3 Oct 2018, Vladis Dronov wrote:
> 
> > This patchset is fixing some aspects of the ring buffer implementation in
> > drivers/hid/hid-debug.c. This implementation has certain problem points:
> > 
> > - it may stuck in an infinite loop
> > - it may return corrupted data
> > - a reader and a writer are not protected by spinlocks, which can lead to
> >   the corrupted data
> > 
> > The suggested patchset is a new ring buffer implementation which overwrites
> > the oldest data in case of an overflow. One can verify the suggested ring
> > buffer implementation by fuzzing it with modified kernel and fuzzer-reader
> > at: https://gist.github.com/nefigtut/33d56e3870b67493cc867344aed2a062
> 
> Vladis,
> 
> thanks for cleaning it up. I actually like your rewrite quite a lot.
> 
> Quick question -- how well was it tested in which scenarios?
> 
> --
> Jiri Kosina
> SUSE Labs

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Harry Cutts @ 2018-10-29 19:17 UTC (permalink / raw)
  To: torvalds
  Cc: jikos, benjamin.tissoires, linux-input, linux-kernel,
	Peter Hutterer
In-Reply-To: <CAHk-=wiRjotiAcy-=Jv5bMM2-r7KzWRGd84CwGmq1FLb1dWKcA@mail.gmail.com>

As I explained in the comments, the reason for triggering at the half
multiplier point and then setting a negative remainder is to cater for
wheels that sometimes rest just before they complete a whole "notch"
in clicky mode. On those mice, setting the threshold at a full notch
means that you frequently have to move the wheel a little past its
resting point to trigger the low-res scroll event.

I don't really understand why the half-multiplier thing would cause
the instability; there's still a low-res threshold every 8 high-res
units whichever way you do it, it's just that with the half-multiplier
method it's 4 closer to the point where the wheel was when the device
was connected. Once you've scrolled around a bit in smooth mode that
should be irrelevant, unless you're somehow managing to move the wheel
in whole-notch increments without the help of the ratchet.

Maybe the long-term solution is to use more than just the value of the
current scroll event when deciding whether to send a low-res event. In
the meantime, I'll write a patch putting the threshold at 7/8ths of a
notch, to see if that can solve both issues.

Thanks,

Harry Cutts
Chrome OS Touch/Input team
Harry Cutts
Chrome OS Touch/Input team


On Mon, 29 Oct 2018 at 11:33, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> On Mon, Oct 29, 2018 at 8:16 AM Linus Torvalds
> <torvalds@linux-foundation.org> wrote:
> >
> > Patch I'm using attached. I'm inclined to just commit it, but if
> > somebody has a better idea, I can test alternatives too.
>
> I committed my patch, as it at least makes the scroll wheel usable.
>
> I'm more than open to further improvements, but I wasn't willing to
> live with the status quo, and carrying this around in my tree as a
> patch kept confusing me while doing merges whenever a conflict
> happened (because the "git diff" that I use to see the conflicts
> obviously also showed my own local modifications).
>
>                  Linus

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Linus Torvalds @ 2018-10-29 18:32 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Harry Cutts, Benjamin Tissoires, linux-input,
	Linux Kernel Mailing List, peter.hutterer
In-Reply-To: <CAHk-=wgwgmcqxChVXQb66itd__qJOBc7LBsJ+nRAM86qJH-S2Q@mail.gmail.com>

On Mon, Oct 29, 2018 at 8:16 AM Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> Patch I'm using attached. I'm inclined to just commit it, but if
> somebody has a better idea, I can test alternatives too.

I committed my patch, as it at least makes the scroll wheel usable.

I'm more than open to further improvements, but I wasn't willing to
live with the status quo, and carrying this around in my tree as a
patch kept confusing me while doing merges whenever a conflict
happened (because the "git diff" that I use to see the conflicts
obviously also showed my own local modifications).

                 Linus

^ permalink raw reply

* [PATCH v5 7/8] dt-bindings: watchdog: document stpmic1 pmic watchdog
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The stpmic1 PMIC embeds a watchdog which is disabled by default.
In case of watchdog, the PMIC goes off.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
changes in v5: nothing

 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt

diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
new file mode 100644
index 0000000..7cc1407
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
@@ -0,0 +1,11 @@
+STMicroelectronics STPMIC1 Watchdog
+
+Required properties:
+
+- compatible : should be "st,stpmic1-wdt"
+
+Example:
+
+watchdog {
+	compatible = "st,stpmic1-wdt";
+};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 8/8] watchdog: stpmic1: add stpmic1 watchdog driver
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The stpmic1 PMIC embeds a watchdog which is disabled by default. As soon
as the watchdog is started, it must be refreshed periodically otherwise
the PMIC goes off.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* initialize wdtdev.parent
* fix checkpatch warning

 drivers/watchdog/Kconfig       |  12 ++++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/stpmic1_wdt.c | 139 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 drivers/watchdog/stpmic1_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5ea8909..6d2ffef 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -806,6 +806,18 @@ config STM32_WATCHDOG
 	  To compile this driver as a module, choose M here: the
 	  module will be called stm32_iwdg.
 
+config STPMIC1_WATCHDOG
+	tristate "STPMIC1 PMIC watchdog support"
+	depends on MFD_STPMIC1
+	select WATCHDOG_CORE
+	help
+	  Say Y here to include watchdog support embedded into STPMIC1 PMIC.
+	  If the watchdog timer expires, stpmic1 will shut down all its power
+	  supplies.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spmic1_wdt.
+
 config UNIPHIER_WATCHDOG
 	tristate "UniPhier watchdog support"
 	depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index bf92e7b..2649cf3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -217,3 +217,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
 obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
 obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
 obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
+obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
new file mode 100644
index 0000000..ad431d8
--- /dev/null
+++ b/drivers/watchdog/stpmic1_wdt.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/kernel.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+/* WATCHDOG CONTROL REGISTER bit */
+#define WDT_START		BIT(0)
+#define WDT_PING		BIT(1)
+#define WDT_START_MASK		BIT(0)
+#define WDT_PING_MASK		BIT(1)
+#define WDT_STOP		0
+
+#define PMIC_WDT_MIN_TIMEOUT 1
+#define PMIC_WDT_MAX_TIMEOUT 256
+#define PMIC_WDT_DEFAULT_TIMEOUT 30
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct stpmic1_wdt {
+	struct stpmic1 *pmic;
+	struct watchdog_device wdtdev;
+};
+
+static int pmic_wdt_start(struct watchdog_device *wdd)
+{
+	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	return regmap_update_bits(wdt->pmic->regmap,
+				  WCHDG_CR, WDT_START_MASK, WDT_START);
+}
+
+static int pmic_wdt_stop(struct watchdog_device *wdd)
+{
+	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	return regmap_update_bits(wdt->pmic->regmap,
+				  WCHDG_CR, WDT_START_MASK, WDT_STOP);
+}
+
+static int pmic_wdt_ping(struct watchdog_device *wdd)
+{
+	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	return regmap_update_bits(wdt->pmic->regmap,
+				  WCHDG_CR, WDT_PING_MASK, WDT_PING);
+}
+
+static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
+				unsigned int timeout)
+{
+	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	/* timeout is equal to register value + 1 */
+	return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
+}
+
+static const struct watchdog_info pmic_watchdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "STPMIC1 PMIC Watchdog",
+};
+
+static const struct watchdog_ops pmic_watchdog_ops = {
+	.owner = THIS_MODULE,
+	.start = pmic_wdt_start,
+	.stop = pmic_wdt_stop,
+	.ping = pmic_wdt_ping,
+	.set_timeout = pmic_wdt_set_timeout,
+};
+
+static int pmic_wdt_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct stpmic1 *pmic;
+	struct stpmic1_wdt *wdt;
+
+	if (!pdev->dev.parent)
+		return -EINVAL;
+
+	pmic = dev_get_drvdata(pdev->dev.parent);
+	if (!pmic)
+		return -EINVAL;
+
+	wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
+	if (!wdt)
+		return -ENOMEM;
+
+	wdt->pmic = pmic;
+
+	wdt->wdtdev.info = &pmic_watchdog_info;
+	wdt->wdtdev.ops = &pmic_watchdog_ops;
+	wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
+	wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
+	wdt->wdtdev.parent = &pdev->dev;
+
+	wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev);
+
+	watchdog_set_nowayout(&wdt->wdtdev, nowayout);
+	watchdog_set_drvdata(&wdt->wdtdev, wdt);
+
+	ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
+	if (ret)
+		return ret;
+
+	dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
+	return 0;
+}
+
+static const struct of_device_id of_pmic_wdt_match[] = {
+	{ .compatible = "st,stpmic1-wdt" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
+
+static struct platform_driver stpmic1_wdt_driver = {
+	.probe = pmic_wdt_probe,
+	.driver = {
+		.name = "stpmic1-wdt",
+		.of_match_table = of_pmic_wdt_match,
+	},
+};
+module_platform_driver(stpmic1_wdt_driver);
+
+MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 5/8] dt-bindings: input: document stpmic1 pmic onkey
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The stpmic1 pmic is able to manage an onkey button. It can be configured
to shut-down the power supplies on a long key-press with an adjustable
duration.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
changes in v5: nothing

 .../devicetree/bindings/input/st,stpmic1-onkey.txt | 28 ++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt

diff --git a/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
new file mode 100644
index 0000000..4494613
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
@@ -0,0 +1,28 @@
+STMicroelectronics STPMIC1 Onkey
+
+Required properties:
+
+- compatible = "st,stpmic1-onkey";
+- interrupts: interrupt line to use
+- interrupt-names = "onkey-falling", "onkey-rising"
+	onkey-falling: happens when onkey is pressed; IT_PONKEY_F of pmic
+	onkey-rising: happens when onkey is released; IT_PONKEY_R of pmic
+
+Optional properties:
+
+- st,onkey-clear-cc-flag: onkey is able power on after an
+  over-current shutdown event.
+- st,onkey-pu-inactive: onkey pull up is not active
+- power-off-time-sec: Duration in seconds which the key should be kept
+        pressed for device to power off automatically (from 1 to 16 seconds).
+        see See Documentation/devicetree/bindings/input/keys.txt
+
+Example:
+
+onkey {
+	compatible = "st,stpmic1-onkey";
+	interrupt-parent = <&pmic>;
+	interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
+	interrupt-names = "onkey-falling", "onkey-rising";
+	power-off-time-sec = <10>;
+};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 6/8] input: stpmic1: add stpmic1 onkey driver
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The stpmic1 pmic is able to manage an onkey button. This driver exposes
the stpmic1 onkey as an input device. It can also be configured to
shut-down the power supplies on a long key-press with an adjustable
duration.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* fix checkpatch warning

 drivers/input/misc/Kconfig         |  11 +++
 drivers/input/misc/Makefile        |   2 +
 drivers/input/misc/stpmic1_onkey.c | 198 +++++++++++++++++++++++++++++++++++++
 3 files changed, 211 insertions(+)
 create mode 100644 drivers/input/misc/stpmic1_onkey.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2b..279fb02 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -851,4 +851,15 @@ config INPUT_SC27XX_VIBRA
 	  To compile this driver as a module, choose M here. The module will
 	  be called sc27xx_vibra.
 
+config INPUT_STPMIC1_ONKEY
+	tristate "STPMIC1 PMIC Onkey support"
+	depends on MFD_STPMIC1
+	help
+	  Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey
+	  can be used to wakeup from low power modes and force a shut-down on
+	  long press.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stpmic1_onkey.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1..1b44202 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
 obj-$(CONFIG_INPUT_SIRFSOC_ONKEY)	+= sirfsoc-onkey.o
 obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY)	+= soc_button_array.o
 obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
+obj-$(CONFIG_INPUT_STPMIC1_ONKEY)  	+= stpmic1_onkey.o
 obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON)	+= tps65218-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
@@ -81,3 +82,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
+
diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c
new file mode 100644
index 0000000..7b49c99
--- /dev/null
+++ b/drivers/input/misc/stpmic1_onkey.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+/**
+ * struct stpmic1_onkey - OnKey data
+ * @input_dev:		pointer to input device
+ * @irq_falling:	irq that we are hooked on to
+ * @irq_rising:		irq that we are hooked on to
+ */
+struct stpmic1_onkey {
+	struct input_dev *input_dev;
+	int irq_falling;
+	int irq_rising;
+};
+
+static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
+{
+	struct stpmic1_onkey *onkey = ponkey;
+	struct input_dev *input_dev = onkey->input_dev;
+
+	input_report_key(input_dev, KEY_POWER, 1);
+	pm_wakeup_event(input_dev->dev.parent, 0);
+	input_sync(input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
+{
+	struct stpmic1_onkey *onkey = ponkey;
+	struct input_dev *input_dev = onkey->input_dev;
+
+	input_report_key(input_dev, KEY_POWER, 0);
+	pm_wakeup_event(input_dev->dev.parent, 0);
+	input_sync(input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int stpmic1_onkey_probe(struct platform_device *pdev)
+{
+	struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct input_dev *input_dev;
+	struct stpmic1_onkey *onkey;
+	unsigned int val, reg = 0;
+	int error;
+
+	onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+	if (!onkey)
+		return -ENOMEM;
+
+	onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
+	if (onkey->irq_falling < 0) {
+		dev_err(dev, "failed: request IRQ onkey-falling %d\n",
+			onkey->irq_falling);
+		return onkey->irq_falling;
+	}
+
+	onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising");
+	if (onkey->irq_rising < 0) {
+		dev_err(dev, "failed: request IRQ onkey-rising %d\n",
+			onkey->irq_rising);
+		return onkey->irq_rising;
+	}
+
+	if (!device_property_read_u32(dev, "power-off-time-sec", &val)) {
+		if (val > 0 && val <= 16) {
+			dev_dbg(dev, "power-off-time=%d seconds\n", val);
+			reg |= PONKEY_PWR_OFF;
+			reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK);
+		} else {
+			dev_err(dev, "power-off-time-sec out of range\n");
+			return -EINVAL;
+		}
+	}
+
+	if (device_property_present(dev, "st,onkey-clear-cc-flag"))
+		reg |= PONKEY_CC_FLAG_CLEAR;
+
+	error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
+				   PONKEY_TURNOFF_MASK, reg);
+	if (error) {
+		dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error);
+		return error;
+	}
+
+	if (device_property_present(dev, "st,onkey-pu-inactive")) {
+		error = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
+					   PONKEY_PU_INACTIVE,
+					   PONKEY_PU_INACTIVE);
+		if (error) {
+			dev_err(dev, "ONKEY Pads configuration failed: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	input_dev = devm_input_allocate_device(dev);
+	if (!input_dev) {
+		dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = "pmic_onkey";
+	input_dev->phys = "pmic_onkey/input0";
+
+	input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+	onkey->input_dev = input_dev;
+
+	/* interrupt is nested in a thread */
+	error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
+					  onkey_falling_irq, IRQF_ONESHOT,
+					  dev_name(dev), onkey);
+	if (error) {
+		dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
+					  onkey_rising_irq, IRQF_ONESHOT,
+					  dev_name(dev), onkey);
+	if (error) {
+		dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(dev, "Can't register power button: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+	device_init_wakeup(dev, true);
+
+	return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev)) {
+		enable_irq_wake(onkey->irq_falling);
+		enable_irq_wake(onkey->irq_rising);
+	}
+	return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(onkey->irq_falling);
+		disable_irq_wake(onkey->irq_rising);
+	}
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm,
+			 stpmic1_onkey_suspend,
+			 stpmic1_onkey_resume);
+
+static const struct of_device_id of_stpmic1_onkey_match[] = {
+	{ .compatible = "st,stpmic1-onkey" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match);
+
+static struct platform_driver stpmic1_onkey_driver = {
+	.probe	= stpmic1_onkey_probe,
+	.driver	= {
+		.name	= "stpmic1_onkey",
+		.of_match_table = of_match_ptr(of_stpmic1_onkey_match),
+		.pm	= &stpmic1_onkey_pm,
+	},
+};
+module_platform_driver(stpmic1_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for STPMIC1");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 4/8] regulator: stpmic1: add stpmic1 regulator driver
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The stpmic1 PMIC embeds several regulators and switches with
different capabilities.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* rework probe: do not modify init data
* merge stpmic1_regulator_init and stpmic1_regulator_parse_dt in the probe fn
* map_mode returns REGULATOR_MODE_INVALID
* add active discharge ops for usb power switches
* move buck modes definition to the binding header

 drivers/regulator/Kconfig             |  12 +
 drivers/regulator/Makefile            |   1 +
 drivers/regulator/stpmic1_regulator.c | 667 ++++++++++++++++++++++++++++++++++
 3 files changed, 680 insertions(+)
 create mode 100644 drivers/regulator/stpmic1_regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6e96ef1..026d480 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -803,6 +803,18 @@ config REGULATOR_STM32_VREFBUF
 	  This driver can also be built as a module. If so, the module
 	  will be called stm32-vrefbuf.
 
+config REGULATOR_STPMIC1
+	tristate "STMicroelectronics STPMIC1 PMIC Regulators"
+	depends on MFD_STPMIC1
+	help
+	  This driver supports STMicroelectronics STPMIC1 PMIC voltage
+	  regulators and switches. The STPMIC1 regulators supply power to
+	  an application processor as well as to external system
+	  peripherals such as DDR, Flash memories and system devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stpmic1_regulator.
+
 config REGULATOR_TI_ABB
 	tristate "TI Adaptive Body Bias on-chip LDO"
 	depends on ARCH_OMAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index eac4d79..300bc4c 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
 obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
 obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
 obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c
new file mode 100644
index 0000000..c96edcd
--- /dev/null
+++ b/drivers/regulator/stpmic1_regulator.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+/**
+ * stpmic1 regulator description
+ * @desc: regulator framework description
+ * @mask_reset_reg: mask reset register address
+ * @mask_reset_mask: mask rank and mask reset register mask
+ * @icc_reg: icc register address
+ * @icc_mask: icc register mask
+ */
+struct stpmic1_regulator_cfg {
+	struct regulator_desc desc;
+	u8 mask_reset_reg;
+	u8 mask_reset_mask;
+	u8 icc_reg;
+	u8 icc_mask;
+};
+
+/**
+ * stpmic1 regulator data: this structure is used as driver data
+ * @regul_id: regulator id
+ * @reg_node: DT node of regulator (unused on non-DT platforms)
+ * @cfg: stpmic specific regulator description
+ * @regmap: point to parent regmap structure
+ */
+struct stpmic1_regulator {
+	unsigned int regul_id;
+	struct device_node *reg_node;
+	struct stpmic1_regulator_cfg *cfg;
+	struct regmap *regmap;
+};
+
+static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode);
+static unsigned int stpmic1_get_mode(struct regulator_dev *rdev);
+static int stpmic1_set_icc(struct regulator_dev *rdev);
+static unsigned int stpmic1_map_mode(unsigned int mode);
+
+enum {
+	STPMIC1_BUCK1 = 0,
+	STPMIC1_BUCK2 = 1,
+	STPMIC1_BUCK3 = 2,
+	STPMIC1_BUCK4 = 3,
+	STPMIC1_LDO1 = 4,
+	STPMIC1_LDO2 = 5,
+	STPMIC1_LDO3 = 6,
+	STPMIC1_LDO4 = 7,
+	STPMIC1_LDO5 = 8,
+	STPMIC1_LDO6 = 9,
+	STPMIC1_VREF_DDR = 10,
+	STPMIC1_BOOST = 11,
+	STPMIC1_VBUS_OTG = 12,
+	STPMIC1_SW_OUT = 13,
+};
+
+/* Enable time worst case is 5000mV/(2250uV/uS) */
+#define PMIC_ENABLE_TIME_US 2200
+
+struct regulator_linear_range buck1_ranges[] = {
+	REGULATOR_LINEAR_RANGE(600000, 0, 30, 25000),
+	REGULATOR_LINEAR_RANGE(1350000, 31, 63, 0),
+};
+
+struct regulator_linear_range buck2_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1000000, 0, 17, 0),
+	REGULATOR_LINEAR_RANGE(1050000, 18, 19, 0),
+	REGULATOR_LINEAR_RANGE(1100000, 20, 21, 0),
+	REGULATOR_LINEAR_RANGE(1150000, 22, 23, 0),
+	REGULATOR_LINEAR_RANGE(1200000, 24, 25, 0),
+	REGULATOR_LINEAR_RANGE(1250000, 26, 27, 0),
+	REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0),
+	REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0),
+	REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0),
+	REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0),
+	REGULATOR_LINEAR_RANGE(1500000, 36, 63, 0),
+};
+
+struct regulator_linear_range buck3_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1000000, 0, 19, 0),
+	REGULATOR_LINEAR_RANGE(1100000, 20, 23, 0),
+	REGULATOR_LINEAR_RANGE(1200000, 24, 27, 0),
+	REGULATOR_LINEAR_RANGE(1300000, 28, 31, 0),
+	REGULATOR_LINEAR_RANGE(1400000, 32, 35, 0),
+	REGULATOR_LINEAR_RANGE(1500000, 36, 55, 100000),
+	REGULATOR_LINEAR_RANGE(3400000, 56, 63, 0),
+};
+
+struct regulator_linear_range buck4_ranges[] = {
+	REGULATOR_LINEAR_RANGE(600000, 0, 27, 25000),
+	REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0),
+	REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0),
+	REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0),
+	REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0),
+	REGULATOR_LINEAR_RANGE(1500000, 36, 60, 100000),
+	REGULATOR_LINEAR_RANGE(3900000, 61, 63, 0),
+};
+
+struct regulator_linear_range ldo1_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+	REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
+};
+
+struct regulator_linear_range ldo2_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+	REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
+};
+
+struct regulator_linear_range ldo3_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+	REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
+	/* with index 31 LDO3 is in DDR mode */
+	REGULATOR_LINEAR_RANGE(500000, 31, 31, 0),
+};
+
+struct regulator_linear_range ldo5_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 8, 30, 100000),
+	REGULATOR_LINEAR_RANGE(3900000, 31, 31, 0),
+};
+
+struct regulator_linear_range ldo6_ranges[] = {
+	REGULATOR_LINEAR_RANGE(900000, 0, 24, 100000),
+	REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
+};
+
+static struct regulator_ops stpmic1_ldo_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+	.set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_ldo3_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_iterate,
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+	.get_bypass = regulator_get_bypass_regmap,
+	.set_bypass = regulator_set_bypass_regmap,
+	.set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_ldo4_fixed_regul_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+	.set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_buck_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+	.set_mode = stpmic1_set_mode,
+	.get_mode = stpmic1_get_mode,
+	.set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_vref_ddr_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+};
+
+static struct regulator_ops stpmic1_boost_regul_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_switch_regul_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.set_over_current_protection = stpmic1_set_icc,
+	.set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+#define REG_LDO(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 32, \
+	.ops = &stpmic1_ldo_ops, \
+	.linear_ranges = base ## _ranges, \
+	.n_linear_ranges = ARRAY_SIZE(base ## _ranges), \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.vsel_reg = ids##_ACTIVE_CR, \
+	.vsel_mask = LDO_VOLTAGE_MASK, \
+	.enable_reg = ids##_ACTIVE_CR, \
+	.enable_mask = LDO_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_LDO3(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 32, \
+	.ops = &stpmic1_ldo3_ops, \
+	.linear_ranges = ldo3_ranges, \
+	.n_linear_ranges = ARRAY_SIZE(ldo3_ranges), \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.vsel_reg = LDO3_ACTIVE_CR, \
+	.vsel_mask = LDO_VOLTAGE_MASK, \
+	.enable_reg = LDO3_ACTIVE_CR, \
+	.enable_mask = LDO_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.bypass_reg = LDO3_ACTIVE_CR, \
+	.bypass_mask = LDO_BYPASS_MASK, \
+	.bypass_val_on = LDO_BYPASS_MASK, \
+	.bypass_val_off = 0, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_LDO4(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmic1_ldo4_fixed_regul_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 3300000, \
+	.fixed_uV = 3300000, \
+	.enable_reg = LDO4_ACTIVE_CR, \
+	.enable_mask = LDO_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_BUCK(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.ops = &stpmic1_buck_ops, \
+	.n_voltages = 64, \
+	.linear_ranges = base ## _ranges, \
+	.n_linear_ranges = ARRAY_SIZE(base ## _ranges), \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.vsel_reg = ids##_ACTIVE_CR, \
+	.vsel_mask = BUCK_VOLTAGE_MASK, \
+	.enable_reg = ids##_ACTIVE_CR, \
+	.enable_mask = BUCK_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.of_map_mode = stpmic1_map_mode, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_VREF_DDR(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmic1_vref_ddr_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 500000, \
+	.fixed_uV = 500000, \
+	.enable_reg = VREF_DDR_ACTIVE_CR, \
+	.enable_mask = BUCK_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_BOOST(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmic1_boost_regul_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 0, \
+	.fixed_uV = 5000000, \
+	.enable_reg = BST_SW_CR, \
+	.enable_mask = BOOST_ENABLED, \
+	.enable_val = BOOST_ENABLED, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.supply_name = #base, \
+}
+
+#define REG_VBUS_OTG(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmic1_switch_regul_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 0, \
+	.fixed_uV = 5000000, \
+	.enable_reg = BST_SW_CR, \
+	.enable_mask = USBSW_OTG_SWITCH_ENABLED, \
+	.enable_val = USBSW_OTG_SWITCH_ENABLED, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.supply_name = #base, \
+	.active_discharge_reg = BST_SW_CR, \
+	.active_discharge_mask = VBUS_OTG_DISCHARGE, \
+	.active_discharge_on = VBUS_OTG_DISCHARGE, \
+}
+
+#define REG_SW_OUT(ids, base) { \
+	.name = #ids, \
+	.id = STPMIC1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmic1_switch_regul_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 0, \
+	.fixed_uV = 5000000, \
+	.enable_reg = BST_SW_CR, \
+	.enable_mask = SWIN_SWOUT_ENABLED, \
+	.enable_val = SWIN_SWOUT_ENABLED, \
+	.disable_val = 0, \
+	.enable_time = PMIC_ENABLE_TIME_US, \
+	.supply_name = #base, \
+	.active_discharge_reg = BST_SW_CR, \
+	.active_discharge_mask = SW_OUT_DISCHARGE, \
+	.active_discharge_on = SW_OUT_DISCHARGE, \
+}
+
+struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = {
+	[STPMIC1_BUCK1] = {
+		.desc = REG_BUCK(BUCK1, buck1),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(0),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(0),
+	},
+	[STPMIC1_BUCK2] = {
+		.desc = REG_BUCK(BUCK2, buck2),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(1),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(1),
+	},
+	[STPMIC1_BUCK3] = {
+		.desc = REG_BUCK(BUCK3, buck3),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(2),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(2),
+	},
+	[STPMIC1_BUCK4] = {
+		.desc = REG_BUCK(BUCK4, buck4),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(3),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(3),
+	},
+	[STPMIC1_LDO1] = {
+		.desc = REG_LDO(LDO1, ldo1),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(0),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(0),
+	},
+	[STPMIC1_LDO2] = {
+		.desc = REG_LDO(LDO2, ldo2),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(1),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(1),
+	},
+	[STPMIC1_LDO3] = {
+		.desc = REG_LDO3(LDO3, ldo3),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(2),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(2),
+	},
+	[STPMIC1_LDO4] = {
+		.desc = REG_LDO4(LDO4, ldo4),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(3),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(3),
+	},
+	[STPMIC1_LDO5] = {
+		.desc = REG_LDO(LDO5, ldo5),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(4),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(4),
+	},
+	[STPMIC1_LDO6] = {
+		.desc = REG_LDO(LDO6, ldo6),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(5),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(5),
+	},
+	[STPMIC1_VREF_DDR] = {
+		.desc = REG_VREF_DDR(VREF_DDR, vref_ddr),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(6),
+	},
+	[STPMIC1_BOOST] = {
+		.desc = REG_BOOST(BOOST, boost),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(6),
+	},
+	[STPMIC1_VBUS_OTG] = {
+		.desc = REG_VBUS_OTG(VBUS_OTG, pwr_sw1),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(4),
+	},
+	[STPMIC1_SW_OUT] = {
+		.desc = REG_SW_OUT(SW_OUT, pwr_sw2),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(5),
+	},
+};
+
+static unsigned int stpmic1_map_mode(unsigned int mode)
+{
+	switch (mode) {
+	case STPMIC1_BUCK_MODE_NORMAL:
+		return REGULATOR_MODE_NORMAL;
+	case STPMIC1_BUCK_MODE_LP:
+		return REGULATOR_MODE_STANDBY;
+	default:
+		return REGULATOR_MODE_INVALID;
+	}
+}
+
+static unsigned int stpmic1_get_mode(struct regulator_dev *rdev)
+{
+	int value;
+
+	regmap_read(rdev->regmap, rdev->desc->enable_reg, &value);
+
+	if (value & STPMIC1_BUCK_MODE_LP)
+		return REGULATOR_MODE_STANDBY;
+
+	return REGULATOR_MODE_NORMAL;
+}
+
+static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	int value;
+
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		value = STPMIC1_BUCK_MODE_NORMAL;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		value = STPMIC1_BUCK_MODE_LP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  STPMIC1_BUCK_MODE_LP, value);
+}
+
+static int stpmic1_set_icc(struct regulator_dev *rdev)
+{
+	struct stpmic1_regulator *regul = rdev_get_drvdata(rdev);
+
+	/* enable switch off in case of over current */
+	return regmap_update_bits(regul->regmap, regul->cfg->icc_reg,
+				  regul->cfg->icc_mask, regul->cfg->icc_mask);
+}
+
+static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data)
+{
+	struct regulator_dev *rdev = (struct regulator_dev *)data;
+
+	mutex_lock(&rdev->mutex);
+
+	/* Send an overcurrent notification */
+	regulator_notifier_call_chain(rdev,
+				      REGULATOR_EVENT_OVER_CURRENT,
+				      NULL);
+
+	mutex_unlock(&rdev->mutex);
+
+	return IRQ_HANDLED;
+}
+
+#define MATCH(_name, _id) \
+	[STPMIC1_##_id] = { \
+		.name = #_name, \
+		.desc = &stpmic1_regulator_cfgs[STPMIC1_##_id].desc, \
+	}
+
+static struct of_regulator_match stpmic1_matches[] = {
+	MATCH(buck1, BUCK1),
+	MATCH(buck2, BUCK2),
+	MATCH(buck3, BUCK3),
+	MATCH(buck4, BUCK4),
+	MATCH(ldo1, LDO1),
+	MATCH(ldo2, LDO2),
+	MATCH(ldo3, LDO3),
+	MATCH(ldo4, LDO4),
+	MATCH(ldo5, LDO5),
+	MATCH(ldo6, LDO6),
+	MATCH(vref_ddr, VREF_DDR),
+	MATCH(boost, BOOST),
+	MATCH(pwr_sw1, VBUS_OTG),
+	MATCH(pwr_sw2, SW_OUT),
+};
+
+static int stpmic1_regulator_register(struct platform_device *pdev, int id,
+				      struct of_regulator_match *match,
+				      struct stpmic1_regulator *regul)
+{
+	struct stpmic1 *pmic_dev = dev_get_drvdata(pdev->dev.parent);
+	struct regulator_dev *rdev;
+	struct regulator_config config = {};
+	int ret = 0;
+	int irq;
+
+	config.dev = &pdev->dev;
+	config.init_data = match->init_data;
+	config.of_node = match->of_node;
+	config.regmap = pmic_dev->regmap;
+	config.driver_data = regul;
+
+	regul->regul_id = id;
+	regul->reg_node = config.of_node;
+	regul->cfg = &stpmic1_regulator_cfgs[id];
+	regul->regmap = pmic_dev->regmap;
+
+	rdev = devm_regulator_register(&pdev->dev, &regul->cfg->desc, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "failed to register %s regulator\n",
+			regul->cfg->desc.name);
+		return PTR_ERR(rdev);
+	}
+
+	/* set mask reset */
+	if (of_get_property(regul->reg_node, "st,mask-reset", NULL) &&
+	    regul->cfg->mask_reset_reg != 0) {
+		ret = regmap_update_bits(regul->regmap,
+					 regul->cfg->mask_reset_reg,
+					 regul->cfg->mask_reset_mask,
+					 regul->cfg->mask_reset_mask);
+		if (ret) {
+			dev_err(&pdev->dev, "set mask reset failed\n");
+			return ret;
+		}
+	}
+
+	/* setup an irq handler for over-current detection */
+	irq = of_irq_get(regul->reg_node, 0);
+	if (irq > 0) {
+		ret = devm_request_threaded_irq(&pdev->dev,
+						irq, NULL,
+						stpmic1_curlim_irq_handler,
+						IRQF_ONESHOT | IRQF_SHARED,
+						pdev->name, rdev);
+		if (ret) {
+			dev_err(&pdev->dev, "Request IRQ failed\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int stpmic1_regulator_probe(struct platform_device *pdev)
+{
+	struct stpmic1_regulator *regul;
+	int i, ret;
+
+	ret = of_regulator_match(&pdev->dev, pdev->dev.of_node, stpmic1_matches,
+				 ARRAY_SIZE(stpmic1_matches));
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Error in PMIC regulator device tree node");
+		return ret;
+	}
+
+	regul = devm_kzalloc(&pdev->dev, ARRAY_SIZE(stpmic1_regulator_cfgs) *
+			     sizeof(struct stpmic1_regulator),
+			     GFP_KERNEL);
+	if (!regul)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(stpmic1_regulator_cfgs); i++) {
+		ret = stpmic1_regulator_register(pdev, i, &stpmic1_matches[i],
+						 regul);
+		if (ret < 0)
+			return ret;
+		regul++;
+	}
+
+	dev_dbg(&pdev->dev, "stpmic1_regulator driver probed\n");
+
+	return 0;
+}
+
+static const struct of_device_id of_pmic_regulator_match[] = {
+	{ .compatible = "st,stpmic1-regulators" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_regulator_match);
+
+static struct platform_driver stpmic1_regulator_driver = {
+	.driver = {
+		.name = "stpmic1-regulator",
+		.of_match_table = of_match_ptr(of_pmic_regulator_match),
+	},
+	.probe = stpmic1_regulator_probe,
+};
+
+module_platform_driver(stpmic1_regulator_driver);
+
+MODULE_DESCRIPTION("STPMIC1 PMIC voltage regulator driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 3/8] dt-bindings: regulator: document stpmic1 pmic regulators
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

The STPMIC1 regulators supply power to the application processor as well as
to the external system peripherals such as DDR, Flash memories and system
devices.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* describe regulator-active-discharge is available for power switches
* remove interrupt parents

 .../bindings/regulator/st,stpmic1-regulator.txt    | 67 ++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt

diff --git a/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
new file mode 100644
index 0000000..5adc938
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
@@ -0,0 +1,67 @@
+STMicroelectronics STPMIC1 Voltage regulators
+
+Regulator Nodes are optional depending on needs.
+
+Available Regulators in STPMIC1 device are:
+  - buck1 for Buck BUCK1
+  - buck2 for Buck BUCK2
+  - buck3 for Buck BUCK3
+  - buck4 for Buck BUCK4
+  - ldo1 for LDO LDO1
+  - ldo2 for LDO LDO2
+  - ldo3 for LDO LDO3
+  - ldo4 for LDO LDO4
+  - ldo5 for LDO LDO5
+  - ldo6 for LDO LDO6
+  - vref_ddr for LDO Vref DDR
+  - boost for Buck BOOST
+  - pwr_sw1 for VBUS_OTG switch
+  - pwr_sw2 for SW_OUT switch
+
+Switches are fixed voltage regulators with only enable/disable capability.
+
+Optional properties:
+- st,mask-reset: mask reset for this regulator: the regulator configuration
+  is maintained during pmic reset.
+- regulator-pull-down: enable high pull down
+  if not specified light pull down is used
+- regulator-over-current-protection:
+    if set, all regulators are switched off in case of over-current detection
+    on this regulator,
+    if not set, the driver only sends an over-current event.
+- interrupt-parent: phandle to the parent interrupt controller
+- interrupts: index of current limit detection interrupt
+- <regulator>-supply: phandle to the parent supply/regulator node
+	each regulator supply can be described except vref_ddr.
+- regulator-active-discharge: can be used on pwr_sw1 and pwr_sw2.
+
+Example:
+regulators {
+	compatible = "st,stpmic1-regulators";
+
+	ldo6-supply = <&v3v3>;
+
+	vdd_core: buck1 {
+		regulator-name = "vdd_core";
+		interrupts = <IT_CURLIM_BUCK1 0>;
+		st,mask-reset;
+		regulator-pull-down;
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	v3v3: buck4 {
+		regulator-name = "v3v3";
+		interrupts = <IT_CURLIM_BUCK4 0>;
+
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	v1v8: ldo6 {
+		regulator-name = "v1v8";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-over-current-protection;
+	};
+};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 2/8] mfd: stpmic1: add stpmic1 driver
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

stpmic1 is a pmic from STMicroelectronics. The STPMIC1 integrates 10
regulators, 3 power switches, a watchdog and an input for a power on key.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* use macro to define regmap register ranges
* use REGMAP_IRQ_REG marco to define interrupts
* remove st properties

 drivers/mfd/Kconfig         |  13 +++
 drivers/mfd/Makefile        |   1 +
 drivers/mfd/stpmic1.c       | 215 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/stpmic1.h | 212 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 444 insertions(+)
 create mode 100644 drivers/mfd/stpmic1.c
 create mode 100644 include/linux/mfd/stpmic1.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11841f4..07e39a6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1855,6 +1855,22 @@ config MFD_STM32_TIMERS
 	  for PWM and IIO Timer. This driver allow to share the
 	  registers between the others drivers.
 
+config MFD_STPMIC1
+	tristate "Support for STPMIC1 PMIC"
+	depends on (I2C=y && OF)
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	select MFD_CORE
+	help
+	  Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 has power on
+	  key, watchdog and regulator functionalities which are supported via
+	  the relevant subsystems.  This driver provides core support for the
+	  STPMIC1, in order to use the actual functionaltiy of the device other
+	  drivers must be enabled.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stpmic1.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5856a94..76fff14 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -232,6 +232,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_STPMIC1)	+= stpmic1.o
 obj-$(CONFIG_MFD_SUN4I_GPADC)	+= sun4i-gpadc.o
 
 obj-$(CONFIG_MFD_STM32_LPTIMER)	+= stm32-lptimer.o
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
new file mode 100644
index 0000000..2f4fd5e
--- /dev/null
+++ b/drivers/mfd/stpmic1.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com>
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+#define STPMIC1_MAIN_IRQ 0
+
+static const struct regmap_range stpmic1_readable_ranges[] = {
+	regmap_reg_range(TURN_ON_SR, VERSION_SR),
+	regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR),
+	regmap_reg_range(BST_SW_CR, BST_SW_CR),
+	regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4),
+	regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4),
+	regmap_reg_range(INT_MASK_R1, INT_MASK_R4),
+	regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4),
+	regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4),
+	regmap_reg_range(INT_SRC_R1, INT_SRC_R1),
+};
+
+static const struct regmap_range stpmic1_writeable_ranges[] = {
+	regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR),
+	regmap_reg_range(BST_SW_CR, BST_SW_CR),
+	regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4),
+	regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4),
+	regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4),
+};
+
+static const struct regmap_range stpmic1_volatile_ranges[] = {
+	regmap_reg_range(TURN_ON_SR, VERSION_SR),
+	regmap_reg_range(WCHDG_CR, WCHDG_CR),
+	regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4),
+	regmap_reg_range(INT_SRC_R1, INT_SRC_R4),
+};
+
+static const struct regmap_access_table stpmic1_readable_table = {
+	.yes_ranges = stpmic1_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(stpmic1_readable_ranges),
+};
+
+static const struct regmap_access_table stpmic1_writeable_table = {
+	.yes_ranges = stpmic1_writeable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(stpmic1_writeable_ranges),
+};
+
+static const struct regmap_access_table stpmic1_volatile_table = {
+	.yes_ranges = stpmic1_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(stpmic1_volatile_ranges),
+};
+
+const struct regmap_config stpmic1_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = PMIC_MAX_REGISTER_ADDRESS,
+	.rd_table = &stpmic1_readable_table,
+	.wr_table = &stpmic1_writeable_table,
+	.volatile_table = &stpmic1_volatile_table,
+};
+
+static const struct regmap_irq stpmic1_irqs[] = {
+	REGMAP_IRQ_REG(IT_PONKEY_F, 0, 0x01),
+	REGMAP_IRQ_REG(IT_PONKEY_R, 0, 0x02),
+	REGMAP_IRQ_REG(IT_WAKEUP_F, 0, 0x04),
+	REGMAP_IRQ_REG(IT_WAKEUP_R, 0, 0x08),
+	REGMAP_IRQ_REG(IT_VBUS_OTG_F, 0, 0x10),
+	REGMAP_IRQ_REG(IT_VBUS_OTG_R, 0, 0x20),
+	REGMAP_IRQ_REG(IT_SWOUT_F, 0, 0x40),
+	REGMAP_IRQ_REG(IT_SWOUT_R, 0, 0x80),
+
+	REGMAP_IRQ_REG(IT_CURLIM_BUCK1, 1, 0x01),
+	REGMAP_IRQ_REG(IT_CURLIM_BUCK2, 1, 0x02),
+	REGMAP_IRQ_REG(IT_CURLIM_BUCK3, 1, 0x04),
+	REGMAP_IRQ_REG(IT_CURLIM_BUCK4, 1, 0x08),
+	REGMAP_IRQ_REG(IT_OCP_OTG, 1, 0x10),
+	REGMAP_IRQ_REG(IT_OCP_SWOUT, 1, 0x20),
+	REGMAP_IRQ_REG(IT_OCP_BOOST, 1, 0x40),
+	REGMAP_IRQ_REG(IT_OVP_BOOST, 1, 0x80),
+
+	REGMAP_IRQ_REG(IT_CURLIM_LDO1, 2, 0x01),
+	REGMAP_IRQ_REG(IT_CURLIM_LDO2, 2, 0x02),
+	REGMAP_IRQ_REG(IT_CURLIM_LDO3, 2, 0x04),
+	REGMAP_IRQ_REG(IT_CURLIM_LDO4, 2, 0x08),
+	REGMAP_IRQ_REG(IT_CURLIM_LDO5, 2, 0x10),
+	REGMAP_IRQ_REG(IT_CURLIM_LDO6, 2, 0x20),
+	REGMAP_IRQ_REG(IT_SHORT_SWOTG, 2, 0x40),
+	REGMAP_IRQ_REG(IT_SHORT_SWOUT, 2, 0x80),
+
+	REGMAP_IRQ_REG(IT_TWARN_F, 3, 0x01),
+	REGMAP_IRQ_REG(IT_TWARN_R, 3, 0x02),
+	REGMAP_IRQ_REG(IT_VINLOW_F, 3, 0x04),
+	REGMAP_IRQ_REG(IT_VINLOW_R, 3, 0x08),
+	REGMAP_IRQ_REG(IT_SWIN_F, 3, 0x40),
+	REGMAP_IRQ_REG(IT_SWIN_R, 3, 0x80),
+};
+
+static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
+	.name = "pmic_irq",
+	.status_base = INT_PENDING_R1,
+	.mask_base = INT_CLEAR_MASK_R1,
+	.unmask_base = INT_SET_MASK_R1,
+	.ack_base = INT_CLEAR_R1,
+	.num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
+	.irqs = stpmic1_irqs,
+	.num_irqs = ARRAY_SIZE(stpmic1_irqs),
+};
+
+static int stpmic1_probe(struct i2c_client *i2c,
+			 const struct i2c_device_id *id)
+{
+	struct stpmic1 *ddata;
+	struct device *dev = &i2c->dev;
+	int ret;
+	struct device_node *np = dev->of_node;
+	u32 reg;
+
+	ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, ddata);
+	ddata->dev = dev;
+
+	ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config);
+	if (IS_ERR(ddata->regmap))
+		return PTR_ERR(ddata->regmap);
+
+	ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ);
+	if (ddata->irq < 0) {
+		dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq);
+		return ddata->irq;
+	}
+
+	/* Read Version ID */
+	ret = regmap_read(ddata->regmap, VERSION_SR, &reg);
+	if (ret) {
+		dev_err(dev, "Unable to read pmic version\n");
+		return ret;
+	}
+	dev_info(dev, "PMIC Chip Version: 0x%x\n", reg);
+
+	/* Initialize PMIC IRQ Chip & IRQ domains associated */
+	ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq,
+				       IRQF_ONESHOT | IRQF_SHARED,
+				       0, &stpmic1_regmap_irq_chip,
+				       &ddata->irq_data);
+	if (ret) {
+		dev_err(dev, "IRQ Chip registration failed: %d\n", ret);
+		return ret;
+	}
+
+	return devm_of_platform_populate(dev);
+}
+
+static const struct i2c_device_id stpmic1_id[] = {
+	{ "stpmic1"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, stpmic1_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int stpmic1_suspend(struct device *dev)
+{
+	struct i2c_client *i2c = to_i2c_client(dev);
+	struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c);
+
+	disable_irq(pmic_dev->irq);
+
+	return 0;
+}
+
+static int stpmic1_resume(struct device *dev)
+{
+	struct i2c_client *i2c = to_i2c_client(dev);
+	struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c);
+	int ret;
+
+	ret = regcache_sync(pmic_dev->regmap);
+	if (ret)
+		return ret;
+
+	enable_irq(pmic_dev->irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume);
+
+static struct i2c_driver stpmic1_driver = {
+	.driver = {
+		.name = "stpmic1",
+		.pm = &stpmic1_pm,
+	},
+	.probe = stpmic1_probe,
+	.id_table = stpmic1_id,
+};
+
+module_i2c_driver(stpmic1_driver);
+
+MODULE_DESCRIPTION("STPMIC1 PMIC Driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h
new file mode 100644
index 0000000..fa3f99f
--- /dev/null
+++ b/include/linux/mfd/stpmic1.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#ifndef __LINUX_MFD_STPMIC1_H
+#define __LINUX_MFD_STPMIC1_H
+
+#define TURN_ON_SR		0x1
+#define TURN_OFF_SR		0x2
+#define ICC_LDO_TURN_OFF_SR	0x3
+#define ICC_BUCK_TURN_OFF_SR	0x4
+#define RREQ_STATE_SR		0x5
+#define VERSION_SR		0x6
+
+#define SWOFF_PWRCTRL_CR	0x10
+#define PADS_PULL_CR		0x11
+#define BUCKS_PD_CR		0x12
+#define LDO14_PD_CR		0x13
+#define LDO56_VREF_PD_CR	0x14
+#define VBUS_DET_VIN_CR		0x15
+#define PKEY_TURNOFF_CR		0x16
+#define BUCKS_MASK_RANK_CR	0x17
+#define BUCKS_MASK_RESET_CR	0x18
+#define LDOS_MASK_RANK_CR	0x19
+#define LDOS_MASK_RESET_CR	0x1A
+#define WCHDG_CR		0x1B
+#define WCHDG_TIMER_CR		0x1C
+#define BUCKS_ICCTO_CR		0x1D
+#define LDOS_ICCTO_CR		0x1E
+
+#define BUCK1_ACTIVE_CR		0x20
+#define BUCK2_ACTIVE_CR		0x21
+#define BUCK3_ACTIVE_CR		0x22
+#define BUCK4_ACTIVE_CR		0x23
+#define VREF_DDR_ACTIVE_CR	0x24
+#define LDO1_ACTIVE_CR		0x25
+#define LDO2_ACTIVE_CR		0x26
+#define LDO3_ACTIVE_CR		0x27
+#define LDO4_ACTIVE_CR		0x28
+#define LDO5_ACTIVE_CR		0x29
+#define LDO6_ACTIVE_CR		0x2A
+
+#define BUCK1_STDBY_CR		0x30
+#define BUCK2_STDBY_CR		0x31
+#define BUCK3_STDBY_CR		0x32
+#define BUCK4_STDBY_CR		0x33
+#define VREF_DDR_STDBY_CR	0x34
+#define LDO1_STDBY_CR		0x35
+#define LDO2_STDBY_CR		0x36
+#define LDO3_STDBY_CR		0x37
+#define LDO4_STDBY_CR		0x38
+#define LDO5_STDBY_CR		0x39
+#define LDO6_STDBY_CR		0x3A
+
+#define BST_SW_CR		0x40
+
+#define INT_PENDING_R1		0x50
+#define INT_PENDING_R2		0x51
+#define INT_PENDING_R3		0x52
+#define INT_PENDING_R4		0x53
+
+#define INT_DBG_LATCH_R1	0x60
+#define INT_DBG_LATCH_R2	0x61
+#define INT_DBG_LATCH_R3	0x62
+#define INT_DBG_LATCH_R4	0x63
+
+#define INT_CLEAR_R1		0x70
+#define INT_CLEAR_R2		0x71
+#define INT_CLEAR_R3		0x72
+#define INT_CLEAR_R4		0x73
+
+#define INT_MASK_R1		0x80
+#define INT_MASK_R2		0x81
+#define INT_MASK_R3		0x82
+#define INT_MASK_R4		0x83
+
+#define INT_SET_MASK_R1		0x90
+#define INT_SET_MASK_R2		0x91
+#define INT_SET_MASK_R3		0x92
+#define INT_SET_MASK_R4		0x93
+
+#define INT_CLEAR_MASK_R1	0xA0
+#define INT_CLEAR_MASK_R2	0xA1
+#define INT_CLEAR_MASK_R3	0xA2
+#define INT_CLEAR_MASK_R4	0xA3
+
+#define INT_SRC_R1		0xB0
+#define INT_SRC_R2		0xB1
+#define INT_SRC_R3		0xB2
+#define INT_SRC_R4		0xB3
+
+#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4
+
+#define STPMIC1_PMIC_NUM_IRQ_REGS 4
+
+#define TURN_OFF_SR_ICC_EVENT	0x08
+
+#define LDO_VOLTAGE_MASK		GENMASK(6, 2)
+#define BUCK_VOLTAGE_MASK		GENMASK(7, 2)
+#define LDO_BUCK_VOLTAGE_SHIFT		2
+
+#define LDO_ENABLE_MASK			BIT(0)
+#define BUCK_ENABLE_MASK		BIT(0)
+
+#define BUCK_HPLP_ENABLE_MASK		BIT(1)
+#define BUCK_HPLP_SHIFT			1
+
+#define STDBY_ENABLE_MASK  BIT(0)
+
+#define BUCKS_PD_CR_REG_MASK	GENMASK(7, 0)
+#define BUCK_MASK_RANK_REGISTER_MASK	GENMASK(3, 0)
+#define BUCK_MASK_RESET_REGISTER_MASK	GENMASK(3, 0)
+#define LDO1234_PULL_DOWN_REGISTER_MASK	GENMASK(7, 0)
+#define LDO56_VREF_PD_CR_REG_MASK	GENMASK(5, 0)
+#define LDO_MASK_RANK_REGISTER_MASK	GENMASK(5, 0)
+#define LDO_MASK_RESET_REGISTER_MASK	GENMASK(5, 0)
+
+#define BUCK1_PULL_DOWN_REG		BUCKS_PD_CR
+#define BUCK1_PULL_DOWN_MASK		BIT(0)
+#define BUCK2_PULL_DOWN_REG		BUCKS_PD_CR
+#define BUCK2_PULL_DOWN_MASK		BIT(2)
+#define BUCK3_PULL_DOWN_REG		BUCKS_PD_CR
+#define BUCK3_PULL_DOWN_MASK		BIT(4)
+#define BUCK4_PULL_DOWN_REG		BUCKS_PD_CR
+#define BUCK4_PULL_DOWN_MASK		BIT(6)
+
+#define LDO1_PULL_DOWN_REG		LDO14_PD_CR
+#define LDO1_PULL_DOWN_MASK		BIT(0)
+#define LDO2_PULL_DOWN_REG		LDO14_PD_CR
+#define LDO2_PULL_DOWN_MASK		BIT(2)
+#define LDO3_PULL_DOWN_REG		LDO14_PD_CR
+#define LDO3_PULL_DOWN_MASK		BIT(4)
+#define LDO4_PULL_DOWN_REG		LDO14_PD_CR
+#define LDO4_PULL_DOWN_MASK		BIT(6)
+#define LDO5_PULL_DOWN_REG		LDO56_VREF_PD_CR
+#define LDO5_PULL_DOWN_MASK		BIT(0)
+#define LDO6_PULL_DOWN_REG		LDO56_VREF_PD_CR
+#define LDO6_PULL_DOWN_MASK		BIT(2)
+#define VREF_DDR_PULL_DOWN_REG		LDO56_VREF_PD_CR
+#define VREF_DDR_PULL_DOWN_MASK		BIT(4)
+
+#define BUCKS_ICCTO_CR_REG_MASK	GENMASK(6, 0)
+#define LDOS_ICCTO_CR_REG_MASK	GENMASK(5, 0)
+
+#define LDO_BYPASS_MASK			BIT(7)
+
+/* Main PMIC Control Register
+ * SWOFF_PWRCTRL_CR
+ * Address : 0x10
+ */
+#define ICC_EVENT_ENABLED		BIT(4)
+#define PWRCTRL_POLARITY_HIGH		BIT(3)
+#define PWRCTRL_PIN_VALID		BIT(2)
+#define RESTART_REQUEST_ENABLED		BIT(1)
+#define SOFTWARE_SWITCH_OFF_ENABLED	BIT(0)
+
+/* Main PMIC PADS Control Register
+ * PADS_PULL_CR
+ * Address : 0x11
+ */
+#define WAKEUP_DETECTOR_DISABLED	BIT(4)
+#define PWRCTRL_PD_ACTIVE		BIT(3)
+#define PWRCTRL_PU_ACTIVE		BIT(2)
+#define WAKEUP_PD_ACTIVE		BIT(1)
+#define PONKEY_PU_INACTIVE		BIT(0)
+
+/* Main PMIC VINLOW Control Register
+ * VBUS_DET_VIN_CRC DMSC
+ * Address : 0x15
+ */
+#define SWIN_DETECTOR_ENABLED		BIT(7)
+#define SWOUT_DETECTOR_ENABLED		BIT(6)
+#define VINLOW_ENABLED			BIT(0)
+#define VINLOW_CTRL_REG_MASK		GENMASK(7, 0)
+
+/* USB Control Register
+ * Address : 0x40
+ */
+#define BOOST_OVP_DISABLED		BIT(7)
+#define VBUS_OTG_DETECTION_DISABLED	BIT(6)
+#define SW_OUT_DISCHARGE		BIT(5)
+#define VBUS_OTG_DISCHARGE		BIT(4)
+#define OCP_LIMIT_HIGH			BIT(3)
+#define SWIN_SWOUT_ENABLED		BIT(2)
+#define USBSW_OTG_SWITCH_ENABLED	BIT(1)
+#define BOOST_ENABLED			BIT(0)
+
+/* PKEY_TURNOFF_CR
+ * Address : 0x16
+ */
+#define PONKEY_PWR_OFF			BIT(7)
+#define PONKEY_CC_FLAG_CLEAR		BIT(6)
+#define PONKEY_TURNOFF_TIMER_MASK	GENMASK(3, 0)
+#define PONKEY_TURNOFF_MASK		GENMASK(7, 0)
+
+/*
+ * struct stpmic1 - stpmic1 master device for sub-drivers
+ * @dev: master device of the chip (can be used to access platform data)
+ * @irq: main IRQ number
+ * @regmap_irq_chip_data: irq chip data
+ */
+struct stpmic1 {
+	struct device *dev;
+	struct regmap *regmap;
+	int irq;
+	struct regmap_irq_chip_data *irq_data;
+};
+
+#endif /*  __LINUX_MFD_STPMIC1_H */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 1/8] dt-bindings: mfd: document stpmic1
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME
In-Reply-To: <1540830122-2577-1-git-send-email-p.paillet@st.com>

stpmic1 is a pmic from STMicroelectronics. The STPMIC1 integrates 10
regulators, 3 power switches, a watchdog and an input for a power on key.

Signed-off-by: Pascal Paillet <p.paillet@st.com>
---
changes in v5:
* removed st properties
* added buck modes definitions in the header

 .../devicetree/bindings/mfd/st,stpmic1.txt         | 61 ++++++++++++++++++++++
 include/dt-bindings/mfd/st,stpmic1.h               | 50 ++++++++++++++++++
 2 files changed, 111 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
 create mode 100644 include/dt-bindings/mfd/st,stpmic1.h

diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
new file mode 100644
index 0000000..afd45c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
@@ -0,0 +1,61 @@
+* STMicroelectronics STPMIC1 Power Management IC
+
+Required properties:
+- compatible:		: "st,stpmic1"
+- reg:			: The I2C slave address for the STPMIC1 chip.
+- interrupts:		: The interrupt line the device is connected to.
+- #interrupt-cells:	: Should be 1.
+- interrupt-controller:	: Marks the device node as an interrupt controller.
+			    Interrupt numbers are defined at
+			    dt-bindings/mfd/st,stpmic1.h.
+
+STPMIC1 consists in a varied group of sub-devices.
+Each sub-device binding is be described in own documentation file.
+
+Device			 Description
+------			------------
+st,stpmic1-onkey	: Power on key, see ../input/st,stpmic1-onkey.txt
+st,stpmic1-regulators	: Regulators, see ../regulator/st,stpmic1-regulator.txt
+st,stpmic1-wdt		: Watchdog, see ../watchdog/st,stpmic1-wdt.txt
+
+Example:
+
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+pmic: pmic@33 {
+	compatible = "st,stpmic1";
+	reg = <0x33>;
+	interrupt-parent = <&gpioa>;
+	interrupts = <0 2>;
+
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	onkey {
+		compatible = "st,stpmic1-onkey";
+		interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
+		interrupt-names = "onkey-falling", "onkey-rising";
+		power-off-time-sec = <10>;
+	};
+
+	watchdog {
+		compatible = "st,stpmic1-wdt";
+	};
+
+	regulators {
+		compatible = "st,stpmic1-regulators";
+
+		vdd_core: buck1 {
+			regulator-name = "vdd_core";
+			regulator-boot-on;
+			regulator-min-microvolt = <700000>;
+			regulator-max-microvolt = <1200000>;
+		};
+		vdd: buck3 {
+			regulator-name = "vdd";
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3300000>;
+			regulator-boot-on;
+			regulator-pull-down;
+		};
+	};
diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h
new file mode 100644
index 0000000..321cd087
--- /dev/null
+++ b/include/dt-bindings/mfd/st,stpmic1.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#ifndef __DT_BINDINGS_STPMIC1_H__
+#define __DT_BINDINGS_STPMIC1_H__
+
+/* IRQ definitions */
+#define IT_PONKEY_F	0
+#define IT_PONKEY_R	1
+#define IT_WAKEUP_F	2
+#define IT_WAKEUP_R	3
+#define IT_VBUS_OTG_F	4
+#define IT_VBUS_OTG_R	5
+#define IT_SWOUT_F	6
+#define IT_SWOUT_R	7
+
+#define IT_CURLIM_BUCK1	8
+#define IT_CURLIM_BUCK2	9
+#define IT_CURLIM_BUCK3	10
+#define IT_CURLIM_BUCK4	11
+#define IT_OCP_OTG	12
+#define IT_OCP_SWOUT	13
+#define IT_OCP_BOOST	14
+#define IT_OVP_BOOST	15
+
+#define IT_CURLIM_LDO1	16
+#define IT_CURLIM_LDO2	17
+#define IT_CURLIM_LDO3	18
+#define IT_CURLIM_LDO4	19
+#define IT_CURLIM_LDO5	20
+#define IT_CURLIM_LDO6	21
+#define IT_SHORT_SWOTG	22
+#define IT_SHORT_SWOUT	23
+
+#define IT_TWARN_F	24
+#define IT_TWARN_R	25
+#define IT_VINLOW_F	26
+#define IT_VINLOW_R	27
+#define IT_SWIN_F	30
+#define IT_SWIN_R	31
+
+/* BUCK MODES definitions */
+#define STPMIC1_BUCK_MODE_NORMAL 0
+#define STPMIC1_BUCK_MODE_LP 2
+
+#endif /* __DT_BINDINGS_STPMIC1_H__ */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 0/8] Introduce STPMIC1 PMIC Driver
From: Pascal PAILLET-LME @ 2018-10-29 16:22 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com, robh+dt@kernel.org,
	mark.rutland@arm.com, lee.jones@linaro.org, lgirdwood@gmail.com,
	broonie@kernel.org, wim@linux-watchdog.org, linux@roeck-us.net,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	benjamin.gaignard@linaro.org, eballetbo@gmail.com,
	axel.lin@ingics.com
  Cc: Pascal PAILLET-LME

The goal of this patch-set is to propose a driver for the STPMIC1 PMIC from 
STMicroelectronics. 
The STPMIC1 regulators supply power to an application processor as well as 
to external system peripherals such as DDR, Flash memories and system
devices. It also features onkey button input and an hardware watchdog.
The STPMIC1 is controlled via I2C. 

Main driver is drivers/mfd/stpmic1 that handle I2C regmap configuration and
irqchip. stpmic1_regulator, stpmic1_onkey and stpmic1_wdt need stpmic1 mfd
as parent.

stpmic1 mfd and regulator drivers maybe mandatory at boot time.

Pascal Paillet (8):
  dt-bindings: mfd: document stpmic1
  mfd: stpmic1: add stpmic1 driver
  dt-bindings: regulator: document stpmic1 pmic regulators
  regulator: stpmic1: add stpmic1 regulator driver
  dt-bindings: input: document stpmic1 pmic onkey
  input: stpmic1: add stpmic1 onkey driver
  dt-bindings: watchdog: document stpmic1 pmic watchdog
  watchdog: stpmic1: add stpmic1 watchdog driver

 .../devicetree/bindings/input/st,stpmic1-onkey.txt |  28 +
 .../devicetree/bindings/mfd/st,stpmic1.txt         |  61 ++
 .../bindings/regulator/st,stpmic1-regulator.txt    |  67 +++
 .../bindings/watchdog/st,stpmic1-wdt.txt           |  11 +
 drivers/input/misc/Kconfig                         |  11 +
 drivers/input/misc/Makefile                        |   2 +
 drivers/input/misc/stpmic1_onkey.c                 | 198 ++++++
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/stpmic1.c                              | 215 +++++++
 drivers/regulator/Kconfig                          |  12 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/stpmic1_regulator.c              | 667 +++++++++++++++++++++
 drivers/watchdog/Kconfig                           |  12 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/stpmic1_wdt.c                     | 139 +++++
 include/dt-bindings/mfd/st,stpmic1.h               |  50 ++
 include/linux/mfd/stpmic1.h                        | 212 +++++++
 18 files changed, 1704 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
 create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
 create mode 100644 drivers/input/misc/stpmic1_onkey.c
 create mode 100644 drivers/mfd/stpmic1.c
 create mode 100644 drivers/regulator/stpmic1_regulator.c
 create mode 100644 drivers/watchdog/stpmic1_wdt.c
 create mode 100644 include/dt-bindings/mfd/st,stpmic1.h
 create mode 100644 include/linux/mfd/stpmic1.h

-- 
1.9.1

^ permalink raw reply

* Re: [PATCH v2] HID: moving to group maintainership model
From: Jiri Kosina @ 2018-10-29 15:28 UTC (permalink / raw)
  To: Benjamin Tissoires; +Cc: open list:HID CORE LAYER, lkml, sfr
In-Reply-To: <CAO-hwJLVY25VXyMKWD6u22gWV3mqgu1iXT3chL3rJ8zXbVbwag@mail.gmail.com>

On Mon, 29 Oct 2018, Benjamin Tissoires wrote:

> > From: Jiri Kosina <jkosina@suse.cz>
> > Date: Sat, 27 Oct 2018 14:16:13 +0200
> > Subject: [PATCH] HID: moving to group maintainership model
> >
> > Benjamin and myself will from now on be sharing maintainership
> > responsibilities for hid.git.
> >
> > Update maintainers to reflect that change, and also move a git
> > repository to shared space at kernel.org.
> >
> > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> 
> I guess I can answer this with:
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> Jiri, would this need to go through the HID tree or some other? (I guess HID).

I've just pushed it to for-4.20/upstream-fixes. Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Linus Torvalds @ 2018-10-29 15:16 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Harry Cutts, Benjamin Tissoires, linux-input,
	Linux Kernel Mailing List, peter.hutterer
In-Reply-To: <nycvar.YFH.7.76.1810291415570.23511@cbobk.fhfr.pm>

[-- Attachment #1: Type: text/plain, Size: 941 bytes --]

On Mon, Oct 29, 2018 at 6:18 AM Jiri Kosina <jikos@kernel.org> wrote:
>
> Benjamin indicated that Peter probably has found the issue in the code
> (failure to properly reset on direction change) that might be causing
> this.

So honestly, once I looked at that hid_scroll_counter_handle_scroll()
function, that's the first thing I tried - get rid of the "half-way
threshold" thing, and reset on direction changes.

It fixes the instability, and I don't see the "back-and-forth"
movements and I don't get the "move the mouse and it generates mouse
wheel events" any more.

It basically makes the wheel _work_ for me.

I'm not entirely convinced it's as good as it used to be, though. It
still feels like it might be a bit over-sensitive, but that may be
because now I'm just looking for it..

Patch I'm using attached. I'm inclined to just commit it, but if
somebody has a better idea, I can test alternatives too.

                    Linus

[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 2208 bytes --]

 drivers/hid/hid-input.c | 43 +++++++++++++++++++++----------------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 567c3bf64515..a2f74e6adc70 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1855,31 +1855,30 @@ EXPORT_SYMBOL_GPL(hidinput_disconnect);
 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
 				      int hi_res_value)
 {
-	int low_res_scroll_amount;
-	/* Some wheels will rest 7/8ths of a notch from the previous notch
-	 * after slow movement, so we want the threshold for low-res events to
-	 * be in the middle of the notches (e.g. after 4/8ths) as opposed to on
-	 * the notches themselves (8/8ths).
-	 */
-	int threshold = counter->resolution_multiplier / 2;
+	int low_res_value, remainder, multiplier;
 
 	input_report_rel(counter->dev, REL_WHEEL_HI_RES,
 			 hi_res_value * counter->microns_per_hi_res_unit);
 
-	counter->remainder += hi_res_value;
-	if (abs(counter->remainder) >= threshold) {
-		/* Add (or subtract) 1 because we want to trigger when the wheel
-		 * is 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;
-	}
+	/*
+	 * Update the low-res remainder with the high-res value,
+	 * but reset if the direction has changed.
+	 */
+	remainder = counter->remainder;
+	if ((remainder ^ hi_res_value) < 0)
+		remainder = 0;
+	remainder += hi_res_value;
+
+	/*
+	 * Then just use the resolution multiplier to see if
+	 * we should send a low-res (aka regular wheel) event.
+	 */
+	multiplier = counter->resolution_multiplier;
+	low_res_value = remainder / multiplier;
+	remainder -= low_res_value * multiplier;
+	counter->remainder = remainder;
+
+	if (low_res_value)
+		input_report_rel(counter->dev, REL_WHEEL, low_res_value);
 }
 EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);

^ permalink raw reply related

* Re: [PATCH v2] HID: moving to group maintainership model
From: Benjamin Tissoires @ 2018-10-29 15:05 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: open list:HID CORE LAYER, lkml, sfr
In-Reply-To: <nycvar.YFH.7.76.1810271425060.23511@cbobk.fhfr.pm>

On Sat, Oct 27, 2018 at 2:25 PM Jiri Kosina <jikos@kernel.org> wrote:
>
> From: Jiri Kosina <jkosina@suse.cz>
> Date: Sat, 27 Oct 2018 14:16:13 +0200
> Subject: [PATCH] HID: moving to group maintainership model
>
> Benjamin and myself will from now on be sharing maintainership
> responsibilities for hid.git.
>
> Update maintainers to reflect that change, and also move a git
> repository to shared space at kernel.org.
>
> Signed-off-by: Jiri Kosina <jkosina@suse.cz>

I guess I can answer this with:
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Jiri, would this need to go through the HID tree or some other? (I guess HID).

> ---
>
> Stephen, could you please update the hid.git URL for linux-next as below?

And thanks Stephen for updating linux-next (and yes, I have read the
linux-next inclusion topic :-P )

Cheers,
Benjamin

> Thanks.
>
> v1->v2: Update all occurences in MAINTAINERS
>
>  MAINTAINERS | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c0c4c5003998..4715cb8d9584 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6588,9 +6588,9 @@ F:        arch/*/include/asm/suspend*.h
>
>  HID CORE LAYER
>  M:     Jiri Kosina <jikos@kernel.org>
> -R:     Benjamin Tissoires <benjamin.tissoires@redhat.com>
> +M:     Benjamin Tissoires <benjamin.tissoires@redhat.com>
>  L:     linux-input@vger.kernel.org
> -T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
> +T:     git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
>  S:     Maintained
>  F:     drivers/hid/
>  F:     include/linux/hid*
> @@ -15254,9 +15254,9 @@ F:      include/linux/usb/gadget*
>
>  USB HID/HIDBP DRIVERS (USB KEYBOARDS, MICE, REMOTE CONTROLS, ...)
>  M:     Jiri Kosina <jikos@kernel.org>
> -R:     Benjamin Tissoires <benjamin.tissoires@redhat.com>
> +M:     Benjamin Tissoires <benjamin.tissoires@redhat.com>
>  L:     linux-usb@vger.kernel.org
> -T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
> +T:     git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
>  S:     Maintained
>  F:     Documentation/hid/hiddev.txt
>  F:     drivers/hid/usbhid/
>
>
> --
> Jiri Kosina
> SUSE Labs
>

^ permalink raw reply

* Re: Logitech high-resolution scrolling..
From: Jiri Kosina @ 2018-10-29 13:18 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Harry Cutts, Benjamin Tissoires, linux-input,
	Linux Kernel Mailing List, Peter Hutterer
In-Reply-To: <CAHk-=wjGKhoVaa60i5XZODEtzTUYVV3fouyYL4k=JsPY4K_xjQ@mail.gmail.com>

On Sun, 28 Oct 2018, Linus Torvalds wrote:

> So I use a Logitech MX Anywhere 2S mouse, and really like it. I have
> the scroll-wheel unlocked, because I like flicking once to scroll a
> lot.
> 
> However, the new high-res scroll code means that the scroll wheel
> action is now much too sensitive. It's not even stable - it will
> scroll back-and-forth a bit occasionally, and in fact it sometimes
> seems to scroll not because I touch the scroll-wheel, but because the
> movement of the mouse itself causes the scroll wheel to move slightly
> and wiggle the scroll action.
> 
> So the recent change to enable the high-res scrolling really seems a
> bit *too* extreme.
> 
> Is there some middle ground that turns the mouse from "look at it
> sideways and it starts scrolling" to something slightly more
> reasonable?

Benjamin indicated that Peter probably has found the issue in the code 
(failure to properly reset on direction change) that might be causing 
this. Adding to CC.

Peter?

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply


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