* [PATCH v4 01/20] dt-bindings: mfd: adp5585: ease on the required properties
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-21 13:02 ` [PATCH v4 02/20] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
` (18 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
It is not mandatory to use all the capabilities of the device. One can
very well only use it as a gpio controller without the PWM support. This
will be even more evident when support for the matrix keymap is added.
Hence drop the requirements for PWM and GPIO.
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
Documentation/devicetree/bindings/mfd/adi,adp5585.yaml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
index ee2272f754a339569c793102928ddd13249f8fee..e30e22f964f78519b2ec207e9415e4897db5c702 100644
--- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
+++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
@@ -52,9 +52,6 @@ patternProperties:
required:
- compatible
- reg
- - gpio-controller
- - "#gpio-cells"
- - "#pwm-cells"
allOf:
- if:
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-05-21 13:02 ` [PATCH v4 01/20] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-23 14:51 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
` (17 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Not all devices (features) of the adp5585 device are mandatory to be
used in all platforms. Hence, check what's given in FW and dynamically
create the mfd_cell array to be given to devm_mfd_add_devices().
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 39 insertions(+), 9 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a3a05f46575 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -17,7 +17,13 @@
#include <linux/regmap.h>
#include <linux/types.h>
-static const struct mfd_cell adp5585_devs[] = {
+enum {
+ ADP5585_DEV_GPIO,
+ ADP5585_DEV_PWM,
+ ADP5585_DEV_MAX
+};
+
+static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
{ .name = "adp5585-gpio", },
{ .name = "adp5585-pwm", },
};
@@ -110,6 +116,37 @@ static const struct regmap_config adp5585_regmap_configs[] = {
},
};
+static void adp5585_remove_devices(void *dev)
+{
+ mfd_remove_devices(dev);
+}
+
+static int adp5585_add_devices(struct device *dev)
+{
+ int ret;
+
+ if (device_property_present(dev, "#pwm-cells")) {
+ ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add pwm device\n");
+ }
+
+ if (device_property_present(dev, "#gpio-cells")) {
+ ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to add gpio device\n");
+ goto out_error;
+ }
+ }
+
+ return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
+out_error:
+ mfd_remove_devices(dev);
+ return ret;
+}
+
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
const struct regmap_config *regmap_config;
@@ -138,14 +175,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
- ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
- adp5585_devs, ARRAY_SIZE(adp5585_devs),
- NULL, 0, NULL);
- if (ret)
- return dev_err_probe(&i2c->dev, ret,
- "Failed to add child devices\n");
-
- return 0;
+ return adp5585_add_devices(&i2c->dev);
}
static int adp5585_suspend(struct device *dev)
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-21 13:02 ` [PATCH v4 02/20] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
@ 2025-05-23 14:51 ` Lee Jones
2025-05-23 15:07 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-05-23 14:51 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Not all devices (features) of the adp5585 device are mandatory to be
> used in all platforms. Hence, check what's given in FW and dynamically
> create the mfd_cell array to be given to devm_mfd_add_devices().
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 39 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a3a05f46575 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -17,7 +17,13 @@
> #include <linux/regmap.h>
> #include <linux/types.h>
>
> -static const struct mfd_cell adp5585_devs[] = {
> +enum {
> + ADP5585_DEV_GPIO,
> + ADP5585_DEV_PWM,
> + ADP5585_DEV_MAX
> +};
> +
> +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> { .name = "adp5585-gpio", },
> { .name = "adp5585-pwm", },
> };
> @@ -110,6 +116,37 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> },
> };
>
> +static void adp5585_remove_devices(void *dev)
> +{
> + mfd_remove_devices(dev);
> +}
> +
> +static int adp5585_add_devices(struct device *dev)
> +{
> + int ret;
> +
> + if (device_property_present(dev, "#pwm-cells")) {
> + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> + &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to add pwm device\n");
PWM is an acronym, it should be capitalised.
> + }
> +
> + if (device_property_present(dev, "#gpio-cells")) {
> + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> + &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
> + if (ret) {
> + ret = dev_err_probe(dev, ret, "Failed to add gpio device\n");
Same with GPIO.
> + goto out_error;
> + }
> + }
> +
> + return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
We have 2 of these now.
Why do we need lots of unbinding functions?
What's wrong .remove() or devm_*()?
> +out_error:
> + mfd_remove_devices(dev);
> + return ret;
> +}
> +
> static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> const struct regmap_config *regmap_config;
> @@ -138,14 +175,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> - ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> - adp5585_devs, ARRAY_SIZE(adp5585_devs),
> - NULL, 0, NULL);
> - if (ret)
> - return dev_err_probe(&i2c->dev, ret,
> - "Failed to add child devices\n");
> -
> - return 0;
> + return adp5585_add_devices(&i2c->dev);
> }
>
> static int adp5585_suspend(struct device *dev)
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-23 14:51 ` Lee Jones
@ 2025-05-23 15:07 ` Nuno Sá
2025-05-23 15:19 ` Lee Jones
0 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá @ 2025-05-23 15:07 UTC (permalink / raw)
To: Lee Jones, nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Fri, 2025-05-23 at 15:51 +0100, Lee Jones wrote:
> On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > Not all devices (features) of the adp5585 device are mandatory to be
> > used in all platforms. Hence, check what's given in FW and dynamically
> > create the mfd_cell array to be given to devm_mfd_add_devices().
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++---------
> > 1 file changed, 39 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a3a05
> > f46575 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -17,7 +17,13 @@
> > #include <linux/regmap.h>
> > #include <linux/types.h>
> >
> > -static const struct mfd_cell adp5585_devs[] = {
> > +enum {
> > + ADP5585_DEV_GPIO,
> > + ADP5585_DEV_PWM,
> > + ADP5585_DEV_MAX
> > +};
> > +
> > +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> > { .name = "adp5585-gpio", },
> > { .name = "adp5585-pwm", },
> > };
> > @@ -110,6 +116,37 @@ static const struct regmap_config
> > adp5585_regmap_configs[] = {
> > },
> > };
> >
> > +static void adp5585_remove_devices(void *dev)
> > +{
> > + mfd_remove_devices(dev);
> > +}
> > +
> > +static int adp5585_add_devices(struct device *dev)
> > +{
> > + int ret;
> > +
> > + if (device_property_present(dev, "#pwm-cells")) {
> > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > + &adp5585_devs[ADP5585_DEV_PWM], 1,
> > NULL, 0, NULL);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to add pwm
> > device\n");
>
> PWM is an acronym, it should be capitalised.
>
> > + }
> > +
> > + if (device_property_present(dev, "#gpio-cells")) {
> > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > + &adp5585_devs[ADP5585_DEV_GPIO], 1,
> > NULL, 0, NULL);
> > + if (ret) {
> > + ret = dev_err_probe(dev, ret, "Failed to add gpio
> > device\n");
>
> Same with GPIO.
>
> > + goto out_error;
> > + }
> > + }
> > +
> > + return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
>
> We have 2 of these now.
>
> Why do we need lots of unbinding functions?
>
> What's wrong .remove() or devm_*()?
I do mention in the cover why I did not used devm_mfd_add_devices(). We would be
adding an action per device and mfd_remove_devices() removes all of them in one
call. Not that is an issue (I believe subsequent calls with be kind of no-ops)
but this way felt more correct.
- Nuno Sá
>
> > +out_error:
> > + mfd_remove_devices(dev);
> > + return ret;
> > +}
> > +
> > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > {
> > const struct regmap_config *regmap_config;
> > @@ -138,14 +175,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return dev_err_probe(&i2c->dev, -ENODEV,
> > "Invalid device ID 0x%02x\n", id);
> >
> > - ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > - adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > - NULL, 0, NULL);
> > - if (ret)
> > - return dev_err_probe(&i2c->dev, ret,
> > - "Failed to add child devices\n");
> > -
> > - return 0;
> > + return adp5585_add_devices(&i2c->dev);
> > }
> >
> > static int adp5585_suspend(struct device *dev)
> >
> > --
> > 2.49.0
> >
> >
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-23 15:07 ` Nuno Sá
@ 2025-05-23 15:19 ` Lee Jones
2025-05-23 15:45 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-05-23 15:19 UTC (permalink / raw)
To: Nuno Sá
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 23 May 2025, Nuno Sá wrote:
> On Fri, 2025-05-23 at 15:51 +0100, Lee Jones wrote:
> > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> >
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > Not all devices (features) of the adp5585 device are mandatory to be
> > > used in all platforms. Hence, check what's given in FW and dynamically
> > > create the mfd_cell array to be given to devm_mfd_add_devices().
> > >
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++---------
> > > 1 file changed, 39 insertions(+), 9 deletions(-)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a3a05
> > > f46575 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -17,7 +17,13 @@
> > > #include <linux/regmap.h>
> > > #include <linux/types.h>
> > >
> > > -static const struct mfd_cell adp5585_devs[] = {
> > > +enum {
> > > + ADP5585_DEV_GPIO,
> > > + ADP5585_DEV_PWM,
> > > + ADP5585_DEV_MAX
> > > +};
> > > +
> > > +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> > > { .name = "adp5585-gpio", },
> > > { .name = "adp5585-pwm", },
> > > };
> > > @@ -110,6 +116,37 @@ static const struct regmap_config
> > > adp5585_regmap_configs[] = {
> > > },
> > > };
> > >
> > > +static void adp5585_remove_devices(void *dev)
> > > +{
> > > + mfd_remove_devices(dev);
> > > +}
> > > +
> > > +static int adp5585_add_devices(struct device *dev)
> > > +{
> > > + int ret;
> > > +
> > > + if (device_property_present(dev, "#pwm-cells")) {
> > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > + &adp5585_devs[ADP5585_DEV_PWM], 1,
> > > NULL, 0, NULL);
> > > + if (ret)
> > > + return dev_err_probe(dev, ret, "Failed to add pwm
> > > device\n");
> >
> > PWM is an acronym, it should be capitalised.
> >
> > > + }
> > > +
> > > + if (device_property_present(dev, "#gpio-cells")) {
> > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > + &adp5585_devs[ADP5585_DEV_GPIO], 1,
> > > NULL, 0, NULL);
> > > + if (ret) {
> > > + ret = dev_err_probe(dev, ret, "Failed to add gpio
> > > device\n");
> >
> > Same with GPIO.
> >
> > > + goto out_error;
> > > + }
> > > + }
> > > +
> > > + return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
> >
> > We have 2 of these now.
> >
> > Why do we need lots of unbinding functions?
> >
> > What's wrong .remove() or devm_*()?
>
> I do mention in the cover why I did not used devm_mfd_add_devices(). We would be
> adding an action per device and mfd_remove_devices() removes all of them in one
> call. Not that is an issue (I believe subsequent calls with be kind of no-ops)
> but this way felt more correct.
I haven't seen another device add a .remove() equivalent per device.
Why do you need it? What's the use-case where this would become critical?
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-23 15:19 ` Lee Jones
@ 2025-05-23 15:45 ` Nuno Sá
2025-06-12 14:17 ` Lee Jones
0 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá @ 2025-05-23 15:45 UTC (permalink / raw)
To: Lee Jones
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 2025-05-23 at 16:19 +0100, Lee Jones wrote:
> On Fri, 23 May 2025, Nuno Sá wrote:
>
> > On Fri, 2025-05-23 at 15:51 +0100, Lee Jones wrote:
> > > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> > >
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > Not all devices (features) of the adp5585 device are mandatory to be
> > > > used in all platforms. Hence, check what's given in FW and dynamically
> > > > create the mfd_cell array to be given to devm_mfd_add_devices().
> > > >
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++-----
> > > > ----
> > > > 1 file changed, 39 insertions(+), 9 deletions(-)
> > > >
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a
> > > > 3a05
> > > > f46575 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -17,7 +17,13 @@
> > > > #include <linux/regmap.h>
> > > > #include <linux/types.h>
> > > >
> > > > -static const struct mfd_cell adp5585_devs[] = {
> > > > +enum {
> > > > + ADP5585_DEV_GPIO,
> > > > + ADP5585_DEV_PWM,
> > > > + ADP5585_DEV_MAX
> > > > +};
> > > > +
> > > > +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> > > > { .name = "adp5585-gpio", },
> > > > { .name = "adp5585-pwm", },
> > > > };
> > > > @@ -110,6 +116,37 @@ static const struct regmap_config
> > > > adp5585_regmap_configs[] = {
> > > > },
> > > > };
> > > >
> > > > +static void adp5585_remove_devices(void *dev)
> > > > +{
> > > > + mfd_remove_devices(dev);
> > > > +}
> > > > +
> > > > +static int adp5585_add_devices(struct device *dev)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + if (device_property_present(dev, "#pwm-cells")) {
> > > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > > + &adp5585_devs[ADP5585_DEV_PWM],
> > > > 1,
> > > > NULL, 0, NULL);
> > > > + if (ret)
> > > > + return dev_err_probe(dev, ret, "Failed to add
> > > > pwm
> > > > device\n");
> > >
> > > PWM is an acronym, it should be capitalised.
> > >
> > > > + }
> > > > +
> > > > + if (device_property_present(dev, "#gpio-cells")) {
> > > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > > + &adp5585_devs[ADP5585_DEV_GPIO],
> > > > 1,
> > > > NULL, 0, NULL);
> > > > + if (ret) {
> > > > + ret = dev_err_probe(dev, ret, "Failed to add
> > > > gpio
> > > > device\n");
> > >
> > > Same with GPIO.
> > >
> > > > + goto out_error;
> > > > + }
> > > > + }
> > > > +
> > > > + return devm_add_action_or_reset(dev, adp5585_remove_devices,
> > > > dev);
> > >
> > > We have 2 of these now.
> > >
> > > Why do we need lots of unbinding functions?
> > >
> > > What's wrong .remove() or devm_*()?
> >
> > I do mention in the cover why I did not used devm_mfd_add_devices(). We
> > would be
> > adding an action per device and mfd_remove_devices() removes all of them in
> > one
> > call. Not that is an issue (I believe subsequent calls with be kind of no-
> > ops)
> > but this way felt more correct.
>
> I haven't seen another device add a .remove() equivalent per device.
>
> Why do you need it? What's the use-case where this would become critical?
No sure I'm following you. I don't need a .remove() per device (or it is not
critical to have one). I just went with this because devm_mfd_add_devices()
would be adding more devres_add() than what we need given that
mfd_remove_devices() removes all child devices at once. So, logically, the above
makes sense to me. Now, I'm ok if you say, don't bother with this and just use
devm_mfd_add_devices() on every device we want to add.
- Nuno Sá
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 02/20] mfd: adp5585: only add devices given in FW
2025-05-23 15:45 ` Nuno Sá
@ 2025-06-12 14:17 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 14:17 UTC (permalink / raw)
To: Nuno Sá
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 23 May 2025, Nuno Sá wrote:
> On Fri, 2025-05-23 at 16:19 +0100, Lee Jones wrote:
> > On Fri, 23 May 2025, Nuno Sá wrote:
> >
> > > On Fri, 2025-05-23 at 15:51 +0100, Lee Jones wrote:
> > > > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> > > >
> > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > >
> > > > > Not all devices (features) of the adp5585 device are mandatory to be
> > > > > used in all platforms. Hence, check what's given in FW and dynamically
> > > > > create the mfd_cell array to be given to devm_mfd_add_devices().
> > > > >
> > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > ---
> > > > > drivers/mfd/adp5585.c | 48 +++++++++++++++++++++++++++++++++++++++-----
> > > > > ----
> > > > > 1 file changed, 39 insertions(+), 9 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > > index
> > > > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..806867c56d6fb4ef1f461af26a424a
> > > > > 3a05
> > > > > f46575 100644
> > > > > --- a/drivers/mfd/adp5585.c
> > > > > +++ b/drivers/mfd/adp5585.c
> > > > > @@ -17,7 +17,13 @@
> > > > > #include <linux/regmap.h>
> > > > > #include <linux/types.h>
> > > > >
> > > > > -static const struct mfd_cell adp5585_devs[] = {
> > > > > +enum {
> > > > > + ADP5585_DEV_GPIO,
> > > > > + ADP5585_DEV_PWM,
> > > > > + ADP5585_DEV_MAX
> > > > > +};
> > > > > +
> > > > > +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> > > > > { .name = "adp5585-gpio", },
> > > > > { .name = "adp5585-pwm", },
> > > > > };
> > > > > @@ -110,6 +116,37 @@ static const struct regmap_config
> > > > > adp5585_regmap_configs[] = {
> > > > > },
> > > > > };
> > > > >
> > > > > +static void adp5585_remove_devices(void *dev)
> > > > > +{
> > > > > + mfd_remove_devices(dev);
> > > > > +}
> > > > > +
> > > > > +static int adp5585_add_devices(struct device *dev)
> > > > > +{
> > > > > + int ret;
> > > > > +
> > > > > + if (device_property_present(dev, "#pwm-cells")) {
> > > > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > > > + &adp5585_devs[ADP5585_DEV_PWM],
> > > > > 1,
> > > > > NULL, 0, NULL);
> > > > > + if (ret)
> > > > > + return dev_err_probe(dev, ret, "Failed to add
> > > > > pwm
> > > > > device\n");
> > > >
> > > > PWM is an acronym, it should be capitalised.
> > > >
> > > > > + }
> > > > > +
> > > > > + if (device_property_present(dev, "#gpio-cells")) {
> > > > > + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > > > > + &adp5585_devs[ADP5585_DEV_GPIO],
> > > > > 1,
> > > > > NULL, 0, NULL);
> > > > > + if (ret) {
> > > > > + ret = dev_err_probe(dev, ret, "Failed to add
> > > > > gpio
> > > > > device\n");
> > > >
> > > > Same with GPIO.
> > > >
> > > > > + goto out_error;
> > > > > + }
> > > > > + }
> > > > > +
> > > > > + return devm_add_action_or_reset(dev, adp5585_remove_devices,
> > > > > dev);
> > > >
> > > > We have 2 of these now.
> > > >
> > > > Why do we need lots of unbinding functions?
> > > >
> > > > What's wrong .remove() or devm_*()?
> > >
> > > I do mention in the cover why I did not used devm_mfd_add_devices(). We
> > > would be
> > > adding an action per device and mfd_remove_devices() removes all of them in
> > > one
> > > call. Not that is an issue (I believe subsequent calls with be kind of no-
> > > ops)
> > > but this way felt more correct.
> >
> > I haven't seen another device add a .remove() equivalent per device.
> >
> > Why do you need it? What's the use-case where this would become critical?
>
> No sure I'm following you. I don't need a .remove() per device (or it is not
> critical to have one). I just went with this because devm_mfd_add_devices()
> would be adding more devres_add() than what we need given that
> mfd_remove_devices() removes all child devices at once. So, logically, the above
> makes sense to me. Now, I'm ok if you say, don't bother with this and just use
> devm_mfd_add_devices() on every device we want to add.
If there is no specific reason for using it this way, I would simply
stick with the usual semantics and devm_* it.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-05-21 13:02 ` [PATCH v4 01/20] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
2025-05-21 13:02 ` [PATCH v4 02/20] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-06-12 14:20 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 04/20] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
` (16 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Make sure to enable the oscillator in the top device. This will allow to
not control this in the child PWM device as that would not work with
future support for keyboard matrix where the oscillator needs to be
always enabled (and so cannot be disabled by disabling PWM).
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 23 +++++++++++++++++++++++
drivers/pwm/pwm-adp5585.c | 5 -----
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 806867c56d6fb4ef1f461af26a424a3a05f46575..f3b74f7d6040413d066eb6dbaecfa3d5e6ee06bd 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -147,6 +147,13 @@ static int adp5585_add_devices(struct device *dev)
return ret;
}
+static void adp5585_osc_disable(void *data)
+{
+ const struct adp5585_dev *adp5585 = data;
+
+ regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
+}
+
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
const struct regmap_config *regmap_config;
@@ -175,6 +182,22 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
+ /*
+ * Enable the internal oscillator, as it's shared between multiple
+ * functions.
+ *
+ * As a future improvement, power consumption could possibly be
+ * decreased in some use cases by enabling and disabling the oscillator
+ * dynamically based on the needs of the child drivers.
+ */
+ ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
+ if (ret)
+ return ret;
+
return adp5585_add_devices(&i2c->dev);
}
diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
index 40472ac5db6410a33e4f790fe8e6c23b517502be..c8821035b7c1412a55a642e6e8a46b66e693a5af 100644
--- a/drivers/pwm/pwm-adp5585.c
+++ b/drivers/pwm/pwm-adp5585.c
@@ -62,7 +62,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
int ret;
if (!state->enabled) {
- regmap_clear_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
return 0;
}
@@ -100,10 +99,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
if (ret)
return ret;
- ret = regmap_set_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
- if (ret)
- return ret;
-
return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
}
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe
2025-05-21 13:02 ` [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-06-12 14:20 ` Lee Jones
2025-06-12 14:40 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-06-12 14:20 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Make sure to enable the oscillator in the top device. This will allow to
> not control this in the child PWM device as that would not work with
> future support for keyboard matrix where the oscillator needs to be
> always enabled (and so cannot be disabled by disabling PWM).
>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 23 +++++++++++++++++++++++
> drivers/pwm/pwm-adp5585.c | 5 -----
> 2 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 806867c56d6fb4ef1f461af26a424a3a05f46575..f3b74f7d6040413d066eb6dbaecfa3d5e6ee06bd 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -147,6 +147,13 @@ static int adp5585_add_devices(struct device *dev)
> return ret;
> }
>
> +static void adp5585_osc_disable(void *data)
> +{
> + const struct adp5585_dev *adp5585 = data;
> +
> + regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> +}
> +
> static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> const struct regmap_config *regmap_config;
> @@ -175,6 +182,22 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> + /*
> + * Enable the internal oscillator, as it's shared between multiple
> + * functions.
> + *
> + * As a future improvement, power consumption could possibly be
> + * decreased in some use cases by enabling and disabling the oscillator
> + * dynamically based on the needs of the child drivers.
This is normal. What's stopping us from doing this from the offset?
> + */
> + ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> + if (ret)
> + return ret;
> +
> return adp5585_add_devices(&i2c->dev);
> }
>
> diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
> index 40472ac5db6410a33e4f790fe8e6c23b517502be..c8821035b7c1412a55a642e6e8a46b66e693a5af 100644
> --- a/drivers/pwm/pwm-adp5585.c
> +++ b/drivers/pwm/pwm-adp5585.c
> @@ -62,7 +62,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
> int ret;
>
> if (!state->enabled) {
> - regmap_clear_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
> regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
> return 0;
> }
> @@ -100,10 +99,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
> if (ret)
> return ret;
>
> - ret = regmap_set_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
> - if (ret)
> - return ret;
> -
> return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
> }
>
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe
2025-06-12 14:20 ` Lee Jones
@ 2025-06-12 14:40 ` Nuno Sá
2025-06-12 15:20 ` Lee Jones
0 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá @ 2025-06-12 14:40 UTC (permalink / raw)
To: Lee Jones, nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Thu, 2025-06-12 at 15:20 +0100, Lee Jones wrote:
> On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > Make sure to enable the oscillator in the top device. This will allow to
> > not control this in the child PWM device as that would not work with
> > future support for keyboard matrix where the oscillator needs to be
> > always enabled (and so cannot be disabled by disabling PWM).
> >
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 23 +++++++++++++++++++++++
> > drivers/pwm/pwm-adp5585.c | 5 -----
> > 2 files changed, 23 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 806867c56d6fb4ef1f461af26a424a3a05f46575..f3b74f7d6040413d066eb6dbaecfa3d5e6
> > ee06bd 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -147,6 +147,13 @@ static int adp5585_add_devices(struct device *dev)
> > return ret;
> > }
> >
> > +static void adp5585_osc_disable(void *data)
> > +{
> > + const struct adp5585_dev *adp5585 = data;
> > +
> > + regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> > +}
> > +
> > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > {
> > const struct regmap_config *regmap_config;
> > @@ -175,6 +182,22 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return dev_err_probe(&i2c->dev, -ENODEV,
> > "Invalid device ID 0x%02x\n", id);
> >
> > + /*
> > + * Enable the internal oscillator, as it's shared between multiple
> > + * functions.
> > + *
> > + * As a future improvement, power consumption could possibly be
> > + * decreased in some use cases by enabling and disabling the
> > oscillator
> > + * dynamically based on the needs of the child drivers.
>
> This is normal. What's stopping us from doing this from the offset?
This is always needed when we have the input device registered. From my testing,
we also need it for GPIOs configured as input. So basically the only reason this
is not being done now is that it would not be trivial or really straightforward
and honestly the series is already big enough :)
Laurent also agreed with this not being mandatory now so hopefully it's also
fine with you.
- Nuno Sá
>
> > + */
> > + ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > ADP5585_OSC_EN);
> > + if (ret)
> > + return ret;
> > +
> > + ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable,
> > adp5585);
> > + if (ret)
> > + return ret;
> > +
> > return adp5585_add_devices(&i2c->dev);
> > }
> >
> > diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
> > index
> > 40472ac5db6410a33e4f790fe8e6c23b517502be..c8821035b7c1412a55a642e6e8a46b66e6
> > 93a5af 100644
> > --- a/drivers/pwm/pwm-adp5585.c
> > +++ b/drivers/pwm/pwm-adp5585.c
> > @@ -62,7 +62,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
> > int ret;
> >
> > if (!state->enabled) {
> > - regmap_clear_bits(regmap, ADP5585_GENERAL_CFG,
> > ADP5585_OSC_EN);
> > regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
> > return 0;
> > }
> > @@ -100,10 +99,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
> > if (ret)
> > return ret;
> >
> > - ret = regmap_set_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
> > - if (ret)
> > - return ret;
> > -
> > return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
> > }
> >
> >
> > --
> > 2.49.0
> >
> >
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe
2025-06-12 14:40 ` Nuno Sá
@ 2025-06-12 15:20 ` Lee Jones
2025-06-13 9:43 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-06-12 15:20 UTC (permalink / raw)
To: Nuno Sá
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Thu, 12 Jun 2025, Nuno Sá wrote:
> On Thu, 2025-06-12 at 15:20 +0100, Lee Jones wrote:
> > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> >
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > Make sure to enable the oscillator in the top device. This will allow to
> > > not control this in the child PWM device as that would not work with
> > > future support for keyboard matrix where the oscillator needs to be
> > > always enabled (and so cannot be disabled by disabling PWM).
> > >
> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 23 +++++++++++++++++++++++
> > > drivers/pwm/pwm-adp5585.c | 5 -----
> > > 2 files changed, 23 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 806867c56d6fb4ef1f461af26a424a3a05f46575..f3b74f7d6040413d066eb6dbaecfa3d5e6
> > > ee06bd 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -147,6 +147,13 @@ static int adp5585_add_devices(struct device *dev)
> > > return ret;
> > > }
> > >
> > > +static void adp5585_osc_disable(void *data)
> > > +{
> > > + const struct adp5585_dev *adp5585 = data;
> > > +
> > > + regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> > > +}
> > > +
> > > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > {
> > > const struct regmap_config *regmap_config;
> > > @@ -175,6 +182,22 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > return dev_err_probe(&i2c->dev, -ENODEV,
> > > "Invalid device ID 0x%02x\n", id);
> > >
> > > + /*
> > > + * Enable the internal oscillator, as it's shared between multiple
> > > + * functions.
> > > + *
> > > + * As a future improvement, power consumption could possibly be
> > > + * decreased in some use cases by enabling and disabling the
> > > oscillator
> > > + * dynamically based on the needs of the child drivers.
> >
> > This is normal. What's stopping us from doing this from the offset?
>
> This is always needed when we have the input device registered. From my testing,
> we also need it for GPIOs configured as input. So basically the only reason this
> is not being done now is that it would not be trivial or really straightforward
> and honestly the series is already big enough :)
Agreed!
> Laurent also agreed with this not being mandatory now so hopefully it's also
> fine with you.
If there is no explicit plan to do this in the future, you may as well
remove the comment. TODOs have a tendency to rot after code is
accepted.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe
2025-06-12 15:20 ` Lee Jones
@ 2025-06-13 9:43 ` Nuno Sá
0 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá @ 2025-06-13 9:43 UTC (permalink / raw)
To: Lee Jones
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Thu, 2025-06-12 at 16:20 +0100, Lee Jones wrote:
> On Thu, 12 Jun 2025, Nuno Sá wrote:
>
> > On Thu, 2025-06-12 at 15:20 +0100, Lee Jones wrote:
> > > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> > >
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > Make sure to enable the oscillator in the top device. This will allow to
> > > > not control this in the child PWM device as that would not work with
> > > > future support for keyboard matrix where the oscillator needs to be
> > > > always enabled (and so cannot be disabled by disabling PWM).
> > > >
> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 23 +++++++++++++++++++++++
> > > > drivers/pwm/pwm-adp5585.c | 5 -----
> > > > 2 files changed, 23 insertions(+), 5 deletions(-)
> > > >
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > 806867c56d6fb4ef1f461af26a424a3a05f46575..f3b74f7d6040413d066eb6dbaecfa3
> > > > d5e6
> > > > ee06bd 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -147,6 +147,13 @@ static int adp5585_add_devices(struct device *dev)
> > > > return ret;
> > > > }
> > > >
> > > > +static void adp5585_osc_disable(void *data)
> > > > +{
> > > > + const struct adp5585_dev *adp5585 = data;
> > > > +
> > > > + regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> > > > +}
> > > > +
> > > > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > > {
> > > > const struct regmap_config *regmap_config;
> > > > @@ -175,6 +182,22 @@ static int adp5585_i2c_probe(struct i2c_client
> > > > *i2c)
> > > > return dev_err_probe(&i2c->dev, -ENODEV,
> > > > "Invalid device ID 0x%02x\n", id);
> > > >
> > > > + /*
> > > > + * Enable the internal oscillator, as it's shared between
> > > > multiple
> > > > + * functions.
> > > > + *
> > > > + * As a future improvement, power consumption could possibly be
> > > > + * decreased in some use cases by enabling and disabling the
> > > > oscillator
> > > > + * dynamically based on the needs of the child drivers.
> > >
> > > This is normal. What's stopping us from doing this from the offset?
> >
> > This is always needed when we have the input device registered. From my
> > testing,
> > we also need it for GPIOs configured as input. So basically the only reason
> > this
> > is not being done now is that it would not be trivial or really
> > straightforward
> > and honestly the series is already big enough :)
>
> Agreed!
>
> > Laurent also agreed with this not being mandatory now so hopefully it's also
> > fine with you.
>
> If there is no explicit plan to do this in the future, you may as well
> remove the comment. TODOs have a tendency to rot after code is
> accepted.
Will do. At least me, I can't commit and guarantee I'll do this in the near
future. So if unless someone can commit to this, I'll remove the TODO in the
next version.
- Nuno Sá
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 04/20] mfd: adp5585: make use of MFD_CELL_NAME()
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (2 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 03/20] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-23 14:53 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 05/20] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
` (15 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Use the helper macro. No functional change intended...
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index f3b74f7d6040413d066eb6dbaecfa3d5e6ee06bd..179dc284833ae8f39eefc6787dd2c7158dfd3ad7 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -4,6 +4,7 @@
*
* Copyright 2022 NXP
* Copyright 2024 Ideas on Board Oy
+ * Copyright 2025 Analog Devices Inc.
*/
#include <linux/array_size.h>
@@ -24,8 +25,8 @@ enum {
};
static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
- { .name = "adp5585-gpio", },
- { .name = "adp5585-pwm", },
+ MFD_CELL_NAME("adp5585-gpio"),
+ MFD_CELL_NAME("adp5585-pwm"),
};
static const struct regmap_range adp5585_volatile_ranges[] = {
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 04/20] mfd: adp5585: make use of MFD_CELL_NAME()
2025-05-21 13:02 ` [PATCH v4 04/20] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-05-23 14:53 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-05-23 14:53 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Use the helper macro. No functional change intended...
"Whilst we're at it, now seems like a good time to update the Copyright."
Or similar.
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index f3b74f7d6040413d066eb6dbaecfa3d5e6ee06bd..179dc284833ae8f39eefc6787dd2c7158dfd3ad7 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -4,6 +4,7 @@
> *
> * Copyright 2022 NXP
> * Copyright 2024 Ideas on Board Oy
> + * Copyright 2025 Analog Devices Inc.
> */
>
> #include <linux/array_size.h>
> @@ -24,8 +25,8 @@ enum {
> };
>
> static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> - { .name = "adp5585-gpio", },
> - { .name = "adp5585-pwm", },
> + MFD_CELL_NAME("adp5585-gpio"),
> + MFD_CELL_NAME("adp5585-pwm"),
> };
>
> static const struct regmap_range adp5585_volatile_ranges[] = {
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 05/20] dt-bindings: mfd: adp5585: document adp5589 I/O expander
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (3 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 04/20] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-21 13:02 ` [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
` (14 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
programmable logic, reset generator, and PWM generator.
We can't really have adp5589 devices fallback to adp5585 (which have
less pins) because there are some significant differences in the register
map.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
.../devicetree/bindings/mfd/adi,adp5585.yaml | 47 +++++++++++++++++-----
.../devicetree/bindings/trivial-devices.yaml | 2 -
2 files changed, 38 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
index e30e22f964f78519b2ec207e9415e4897db5c702..9471af28419d820424745315ffb2129f7dd37581 100644
--- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
+++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
@@ -15,14 +15,21 @@ description:
properties:
compatible:
- items:
- - enum:
- - adi,adp5585-00 # Default
- - adi,adp5585-01 # 11 GPIOs
- - adi,adp5585-02 # No pull-up resistors by default on special pins
- - adi,adp5585-03 # Alternate I2C address
- - adi,adp5585-04 # Pull-down resistors on all pins by default
- - const: adi,adp5585
+ oneOf:
+ - items:
+ - enum:
+ - adi,adp5585-00 # Default
+ - adi,adp5585-01 # 11 GPIOs
+ - adi,adp5585-02 # No pull-up resistors by default on special pins
+ - adi,adp5585-03 # Alternate I2C address
+ - adi,adp5585-04 # Pull-down resistors on all pins by default
+ - const: adi,adp5585
+ - items:
+ - enum:
+ - adi,adp5589-00 # Default
+ - adi,adp5589-01 # R4 defaulted to RESET1 output
+ - adi,adp5589-02 # Pull-down resistors by default on special pins
+ - const: adi,adp5589
reg:
maxItems: 1
@@ -62,7 +69,17 @@ allOf:
then:
properties:
gpio-reserved-ranges: false
- else:
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,adp5585-00
+ - adi,adp5585-02
+ - adi,adp5585-03
+ - adi,adp5585-04
+ then:
properties:
gpio-reserved-ranges:
maxItems: 1
@@ -71,6 +88,18 @@ allOf:
- const: 5
- const: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,adp5589-00
+ - adi,adp5589-01
+ - adi,adp5589-02
+ then:
+ properties:
+ gpio-reserved-ranges: false
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 8da408107e55483affedb7e697eb79e8c8902ed9..208fe4242672d9da66799c2742a9381938737232 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -39,8 +39,6 @@ properties:
- ad,adm9240
# AD5110 - Nonvolatile Digital Potentiometer
- adi,ad5110
- # Analog Devices ADP5589 Keypad Decoder and I/O Expansion
- - adi,adp5589
# Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher
- adi,lt7182s
# AMS iAQ-Core VOC Sensor
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (4 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 05/20] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-23 15:03 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 07/20] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
` (13 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The only thing changing between variants is the regmap default
registers. Hence, instead of having a regmap condig for every variant
(duplicating lots of fields), add a chip info type of structure with a
regmap id to identify which defaults to use and populate regmap_config
at runtime given a template plus the id. Also note that between
variants, the defaults can be the same which means the chip info
structure can be used in more than one compatible.
This will also make it simpler adding new chips with more variants.
Also note that the chip info structures are deliberately not const as
they will also contain lots of members that are the same between the
different devices variants and so we will fill those at runtime.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 74 +++++++++++++++++++++------------------------
include/linux/mfd/adp5585.h | 10 ++++++
2 files changed, 44 insertions(+), 40 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 179dc284833ae8f39eefc6787dd2c7158dfd3ad7..672f3468bda5be6af85a5982c3626053b4cb59bf 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -81,42 +81,31 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
};
-enum adp5585_regmap_type {
- ADP5585_REGMAP_00,
- ADP5585_REGMAP_02,
- ADP5585_REGMAP_04,
+/* -1 since the enum starts at 1 for error checking in i2c_get_match_data()*/
+static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
+ [ADP5585_00 - 1] = adp5585_regmap_defaults_00,
+ [ADP5585_01 - 1] = adp5585_regmap_defaults_00,
+ [ADP5585_02 - 1] = adp5585_regmap_defaults_02,
+ [ADP5585_03 - 1] = adp5585_regmap_defaults_00,
+ [ADP5585_04 - 1] = adp5585_regmap_defaults_04,
};
-static const struct regmap_config adp5585_regmap_configs[] = {
- [ADP5585_REGMAP_00] = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = ADP5585_MAX_REG,
- .volatile_table = &adp5585_volatile_regs,
- .cache_type = REGCACHE_MAPLE,
- .reg_defaults_raw = adp5585_regmap_defaults_00,
- .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_00),
- },
- [ADP5585_REGMAP_02] = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = ADP5585_MAX_REG,
- .volatile_table = &adp5585_volatile_regs,
- .cache_type = REGCACHE_MAPLE,
- .reg_defaults_raw = adp5585_regmap_defaults_02,
- .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_02),
- },
- [ADP5585_REGMAP_04] = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = ADP5585_MAX_REG,
- .volatile_table = &adp5585_volatile_regs,
- .cache_type = REGCACHE_MAPLE,
- .reg_defaults_raw = adp5585_regmap_defaults_04,
- .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
- },
+static const struct regmap_config adp5585_regmap_config_template = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5585_MAX_REG,
+ .volatile_table = &adp5585_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
};
+static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
+ struct regmap_config *regmap_config)
+{
+ *regmap_config = adp5585_regmap_config_template;
+ regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant - 1];
+}
+
static void adp5585_remove_devices(void *dev)
{
mfd_remove_devices(dev);
@@ -157,7 +146,7 @@ static void adp5585_osc_disable(void *data)
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
- const struct regmap_config *regmap_config;
+ struct regmap_config regmap_config;
struct adp5585_dev *adp5585;
unsigned int id;
int ret;
@@ -168,8 +157,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, adp5585);
- regmap_config = i2c_get_match_data(i2c);
- adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c);
+ if (!adp5585->variant)
+ return -ENODEV;
+
+ adp5585_fill_regmap_config(adp5585, ®map_config);
+
+ adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
if (IS_ERR(adp5585->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
"Failed to initialize register map\n");
@@ -226,19 +220,19 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
static const struct of_device_id adp5585_of_match[] = {
{
.compatible = "adi,adp5585-00",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .data = (void *)ADP5585_00,
}, {
.compatible = "adi,adp5585-01",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .data = (void *)ADP5585_01,
}, {
.compatible = "adi,adp5585-02",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
+ .data = (void *)ADP5585_02,
}, {
.compatible = "adi,adp5585-03",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .data = (void *)ADP5585_03,
}, {
.compatible = "adi,adp5585-04",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
+ .data = (void *)ADP5585_04,
},
{ /* sentinel */ }
};
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 016033cd68e46757aca86d21dd37025fd354b801..2813b20e638b6e73ef198e43af07ef29ff25f273 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -119,8 +119,18 @@
struct regmap;
+enum adp5585_variant {
+ ADP5585_00 = 1,
+ ADP5585_01,
+ ADP5585_02,
+ ADP5585_03,
+ ADP5585_04,
+ ADP5585_MAX
+};
+
struct adp5585_dev {
struct regmap *regmap;
+ enum adp5585_variant variant;
};
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled
2025-05-21 13:02 ` [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
@ 2025-05-23 15:03 ` Lee Jones
2025-05-27 10:08 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-05-23 15:03 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The only thing changing between variants is the regmap default
> registers. Hence, instead of having a regmap condig for every variant
Spellcheck.
> (duplicating lots of fields), add a chip info type of structure with a
> regmap id to identify which defaults to use and populate regmap_config
"ID"
> at runtime given a template plus the id. Also note that between
> variants, the defaults can be the same which means the chip info
> structure can be used in more than one compatible.
>
> This will also make it simpler adding new chips with more variants.
>
> Also note that the chip info structures are deliberately not const as
> they will also contain lots of members that are the same between the
> different devices variants and so we will fill those at runtime.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 74 +++++++++++++++++++++------------------------
> include/linux/mfd/adp5585.h | 10 ++++++
> 2 files changed, 44 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 179dc284833ae8f39eefc6787dd2c7158dfd3ad7..672f3468bda5be6af85a5982c3626053b4cb59bf 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -81,42 +81,31 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
> /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
> };
>
> -enum adp5585_regmap_type {
> - ADP5585_REGMAP_00,
> - ADP5585_REGMAP_02,
> - ADP5585_REGMAP_04,
> +/* -1 since the enum starts at 1 for error checking in i2c_get_match_data()*/
Space before the '*'.
> +static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
> + [ADP5585_00 - 1] = adp5585_regmap_defaults_00,
> + [ADP5585_01 - 1] = adp5585_regmap_defaults_00,
> + [ADP5585_02 - 1] = adp5585_regmap_defaults_02,
> + [ADP5585_03 - 1] = adp5585_regmap_defaults_00,
> + [ADP5585_04 - 1] = adp5585_regmap_defaults_04,
Just leave the first entry blank. No need for all he gymnastics.
> };
>
> -static const struct regmap_config adp5585_regmap_configs[] = {
> - [ADP5585_REGMAP_00] = {
> - .reg_bits = 8,
> - .val_bits = 8,
> - .max_register = ADP5585_MAX_REG,
> - .volatile_table = &adp5585_volatile_regs,
> - .cache_type = REGCACHE_MAPLE,
> - .reg_defaults_raw = adp5585_regmap_defaults_00,
> - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_00),
> - },
> - [ADP5585_REGMAP_02] = {
> - .reg_bits = 8,
> - .val_bits = 8,
> - .max_register = ADP5585_MAX_REG,
> - .volatile_table = &adp5585_volatile_regs,
> - .cache_type = REGCACHE_MAPLE,
> - .reg_defaults_raw = adp5585_regmap_defaults_02,
> - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_02),
> - },
> - [ADP5585_REGMAP_04] = {
> - .reg_bits = 8,
> - .val_bits = 8,
> - .max_register = ADP5585_MAX_REG,
> - .volatile_table = &adp5585_volatile_regs,
> - .cache_type = REGCACHE_MAPLE,
> - .reg_defaults_raw = adp5585_regmap_defaults_04,
> - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> - },
> +static const struct regmap_config adp5585_regmap_config_template = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ADP5585_MAX_REG,
> + .volatile_table = &adp5585_volatile_regs,
> + .cache_type = REGCACHE_MAPLE,
> + .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
> };
>
> +static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> + struct regmap_config *regmap_config)
> +{
> + *regmap_config = adp5585_regmap_config_template;
Return struct regmap_config * instead.
> + regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant - 1];
Does this really warrant a separate function?
> +}
> +
> static void adp5585_remove_devices(void *dev)
> {
> mfd_remove_devices(dev);
> @@ -157,7 +146,7 @@ static void adp5585_osc_disable(void *data)
>
> static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> - const struct regmap_config *regmap_config;
> + struct regmap_config regmap_config;
> struct adp5585_dev *adp5585;
> unsigned int id;
> int ret;
> @@ -168,8 +157,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>
> i2c_set_clientdata(i2c, adp5585);
>
> - regmap_config = i2c_get_match_data(i2c);
> - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> + adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c);
> + if (!adp5585->variant)
> + return -ENODEV;
> +
> + adp5585_fill_regmap_config(adp5585, ®map_config);
> +
> + adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> "Failed to initialize register map\n");
> @@ -226,19 +220,19 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
> static const struct of_device_id adp5585_of_match[] = {
> {
> .compatible = "adi,adp5585-00",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .data = (void *)ADP5585_00,
> }, {
> .compatible = "adi,adp5585-01",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .data = (void *)ADP5585_01,
> }, {
> .compatible = "adi,adp5585-02",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> + .data = (void *)ADP5585_02,
> }, {
> .compatible = "adi,adp5585-03",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .data = (void *)ADP5585_03,
> }, {
> .compatible = "adi,adp5585-04",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> + .data = (void *)ADP5585_04,
> },
> { /* sentinel */ }
> };
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 016033cd68e46757aca86d21dd37025fd354b801..2813b20e638b6e73ef198e43af07ef29ff25f273 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -119,8 +119,18 @@
>
> struct regmap;
>
> +enum adp5585_variant {
> + ADP5585_00 = 1,
> + ADP5585_01,
> + ADP5585_02,
> + ADP5585_03,
> + ADP5585_04,
> + ADP5585_MAX
> +};
> +
> struct adp5585_dev {
> struct regmap *regmap;
> + enum adp5585_variant variant;
> };
>
> #endif
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled
2025-05-23 15:03 ` Lee Jones
@ 2025-05-27 10:08 ` Nuno Sá
0 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá @ 2025-05-27 10:08 UTC (permalink / raw)
To: Lee Jones, nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Fri, 2025-05-23 at 16:03 +0100, Lee Jones wrote:
> On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > The only thing changing between variants is the regmap default
> > registers. Hence, instead of having a regmap condig for every variant
>
> Spellcheck.
>
> > (duplicating lots of fields), add a chip info type of structure with a
> > regmap id to identify which defaults to use and populate regmap_config
>
> "ID"
>
> > at runtime given a template plus the id. Also note that between
> > variants, the defaults can be the same which means the chip info
> > structure can be used in more than one compatible.
> >
> > This will also make it simpler adding new chips with more variants.
> >
> > Also note that the chip info structures are deliberately not const as
> > they will also contain lots of members that are the same between the
> > different devices variants and so we will fill those at runtime.
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 74 +++++++++++++++++++++---------------------
> > ---
> > include/linux/mfd/adp5585.h | 10 ++++++
> > 2 files changed, 44 insertions(+), 40 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 179dc284833ae8f39eefc6787dd2c7158dfd3ad7..672f3468bda5be6af85a5982c3626053b4
> > cb59bf 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -81,42 +81,31 @@ static const u8
> > adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
> > /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
> > };
> >
> > -enum adp5585_regmap_type {
> > - ADP5585_REGMAP_00,
> > - ADP5585_REGMAP_02,
> > - ADP5585_REGMAP_04,
> > +/* -1 since the enum starts at 1 for error checking in
> > i2c_get_match_data()*/
>
> Space before the '*'.
>
> > +static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
> > + [ADP5585_00 - 1] = adp5585_regmap_defaults_00,
> > + [ADP5585_01 - 1] = adp5585_regmap_defaults_00,
> > + [ADP5585_02 - 1] = adp5585_regmap_defaults_02,
> > + [ADP5585_03 - 1] = adp5585_regmap_defaults_00,
> > + [ADP5585_04 - 1] = adp5585_regmap_defaults_04,
>
> Just leave the first entry blank. No need for all he gymnastics.
alright
>
> > };
> >
> > -static const struct regmap_config adp5585_regmap_configs[] = {
> > - [ADP5585_REGMAP_00] = {
> > - .reg_bits = 8,
> > - .val_bits = 8,
> > - .max_register = ADP5585_MAX_REG,
> > - .volatile_table = &adp5585_volatile_regs,
> > - .cache_type = REGCACHE_MAPLE,
> > - .reg_defaults_raw = adp5585_regmap_defaults_00,
> > - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_00),
> > - },
> > - [ADP5585_REGMAP_02] = {
> > - .reg_bits = 8,
> > - .val_bits = 8,
> > - .max_register = ADP5585_MAX_REG,
> > - .volatile_table = &adp5585_volatile_regs,
> > - .cache_type = REGCACHE_MAPLE,
> > - .reg_defaults_raw = adp5585_regmap_defaults_02,
> > - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_02),
> > - },
> > - [ADP5585_REGMAP_04] = {
> > - .reg_bits = 8,
> > - .val_bits = 8,
> > - .max_register = ADP5585_MAX_REG,
> > - .volatile_table = &adp5585_volatile_regs,
> > - .cache_type = REGCACHE_MAPLE,
> > - .reg_defaults_raw = adp5585_regmap_defaults_04,
> > - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> > - },
> > +static const struct regmap_config adp5585_regmap_config_template = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ADP5585_MAX_REG,
> > + .volatile_table = &adp5585_volatile_regs,
> > + .cache_type = REGCACHE_MAPLE,
> > + .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
> > };
> >
> > +static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > + struct regmap_config *regmap_config)
> > +{
> > + *regmap_config = adp5585_regmap_config_template;
>
> Return struct regmap_config * instead.
>
> > + regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585-
> > >variant - 1];
>
> Does this really warrant a separate function?
In this particular patch, not really. But since I know how things will evolve :)
- Nuno Sá
>
> > +}
> > +
> > static void adp5585_remove_devices(void *dev)
> > {
> > mfd_remove_devices(dev);
> > @@ -157,7 +146,7 @@ static void adp5585_osc_disable(void *data)
> >
> > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > {
> > - const struct regmap_config *regmap_config;
> > + struct regmap_config regmap_config;
> > struct adp5585_dev *adp5585;
> > unsigned int id;
> > int ret;
> > @@ -168,8 +157,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >
> > i2c_set_clientdata(i2c, adp5585);
> >
> > - regmap_config = i2c_get_match_data(i2c);
> > - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> > + adp5585->variant = (enum
> > adp5585_variant)(uintptr_t)i2c_get_match_data(i2c);
> > + if (!adp5585->variant)
> > + return -ENODEV;
> > +
> > + adp5585_fill_regmap_config(adp5585, ®map_config);
> > +
> > + adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> > if (IS_ERR(adp5585->regmap))
> > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > "Failed to initialize register
> > map\n");
> > @@ -226,19 +220,19 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm,
> > adp5585_suspend, adp5585_resume);
> > static const struct of_device_id adp5585_of_match[] = {
> > {
> > .compatible = "adi,adp5585-00",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .data = (void *)ADP5585_00,
> > }, {
> > .compatible = "adi,adp5585-01",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .data = (void *)ADP5585_01,
> > }, {
> > .compatible = "adi,adp5585-02",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > + .data = (void *)ADP5585_02,
> > }, {
> > .compatible = "adi,adp5585-03",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .data = (void *)ADP5585_03,
> > }, {
> > .compatible = "adi,adp5585-04",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > + .data = (void *)ADP5585_04,
> > },
> > { /* sentinel */ }
> > };
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index
> > 016033cd68e46757aca86d21dd37025fd354b801..2813b20e638b6e73ef198e43af07ef29ff
> > 25f273 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -119,8 +119,18 @@
> >
> > struct regmap;
> >
> > +enum adp5585_variant {
> > + ADP5585_00 = 1,
> > + ADP5585_01,
> > + ADP5585_02,
> > + ADP5585_03,
> > + ADP5585_04,
> > + ADP5585_MAX
> > +};
> > +
> > struct adp5585_dev {
> > struct regmap *regmap;
> > + enum adp5585_variant variant;
> > };
> >
> > #endif
> >
> > --
> > 2.49.0
> >
> >
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 07/20] mfd: adp5585: add support for adp5589
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (5 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 06/20] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-23 15:15 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 08/20] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
` (12 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
programmable logic, reset generator, and PWM generator.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 139 +++++++++++++++++++++++++++++++++++++++-----
include/linux/mfd/adp5585.h | 11 ++++
2 files changed, 134 insertions(+), 16 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 672f3468bda5be6af85a5982c3626053b4cb59bf..53ac46365e56874a05855f1df1843717148f181a 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -29,6 +29,11 @@ static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
MFD_CELL_NAME("adp5585-pwm"),
};
+static const struct mfd_cell adp5589_devs[] = {
+ MFD_CELL_NAME("adp5589-gpio"),
+ MFD_CELL_NAME("adp5589-pwm"),
+};
+
static const struct regmap_range adp5585_volatile_ranges[] = {
regmap_reg_range(ADP5585_ID, ADP5585_GPI_STATUS_B),
};
@@ -38,6 +43,15 @@ static const struct regmap_access_table adp5585_volatile_regs = {
.n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges),
};
+static const struct regmap_range adp5589_volatile_ranges[] = {
+ regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C),
+};
+
+static const struct regmap_access_table adp5589_volatile_regs = {
+ .yes_ranges = adp5589_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges),
+};
+
/*
* Chip variants differ in the default configuration of pull-up and pull-down
* resistors, and therefore have different default register values:
@@ -81,6 +95,45 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
};
+static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+};
+
+static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
/* -1 since the enum starts at 1 for error checking in i2c_get_match_data()*/
static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
[ADP5585_00 - 1] = adp5585_regmap_defaults_00,
@@ -88,6 +141,9 @@ static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
[ADP5585_02 - 1] = adp5585_regmap_defaults_02,
[ADP5585_03 - 1] = adp5585_regmap_defaults_00,
[ADP5585_04 - 1] = adp5585_regmap_defaults_04,
+ [ADP5589_00 - 1] = adp5589_regmap_defaults_00,
+ [ADP5589_01 - 1] = adp5589_regmap_defaults_01,
+ [ADP5589_02 - 1] = adp5589_regmap_defaults_02,
};
static const struct regmap_config adp5585_regmap_config_template = {
@@ -99,11 +155,39 @@ static const struct regmap_config adp5585_regmap_config_template = {
.num_reg_defaults_raw = ADP5585_MAX_REG + 1,
};
-static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
+static const struct regmap_config adp5589_regmap_config_template = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5589_MAX_REG,
+ .volatile_table = &adp5589_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = ADP5589_MAX_REG + 1,
+};
+
+static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
struct regmap_config *regmap_config)
{
- *regmap_config = adp5585_regmap_config_template;
+ switch (adp5585->variant) {
+ case ADP5585_00:
+ case ADP5585_01:
+ case ADP5585_02:
+ case ADP5585_03:
+ case ADP5585_04:
+ *regmap_config = adp5585_regmap_config_template;
+ adp5585->id = ADP5585_MAN_ID_VALUE;
+ break;
+ case ADP5589_00:
+ case ADP5589_01:
+ case ADP5589_02:
+ *regmap_config = adp5589_regmap_config_template;
+ adp5585->id = ADP5589_MAN_ID_VALUE;
+ break;
+ default:
+ return -ENODEV;
+ }
+
regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant - 1];
+ return 0;
}
static void adp5585_remove_devices(void *dev)
@@ -111,29 +195,35 @@ static void adp5585_remove_devices(void *dev)
mfd_remove_devices(dev);
}
-static int adp5585_add_devices(struct device *dev)
+static int adp5585_add_devices(const struct adp5585_dev *adp5585)
{
+ const struct mfd_cell *cells;
int ret;
- if (device_property_present(dev, "#pwm-cells")) {
- ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
- &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
+ if (adp5585->id == ADP5585_MAN_ID_VALUE)
+ cells = adp5585_devs;
+ else
+ cells = adp5589_devs;
+
+ if (device_property_present(adp5585->dev, "#pwm-cells")) {
+ ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add pwm device\n");
+ return dev_err_probe(adp5585->dev, ret, "Failed to add pwm device\n");
}
- if (device_property_present(dev, "#gpio-cells")) {
- ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
- &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
+ if (device_property_present(adp5585->dev, "#gpio-cells")) {
+ ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
if (ret) {
- ret = dev_err_probe(dev, ret, "Failed to add gpio device\n");
+ ret = dev_err_probe(adp5585->dev, ret, "Failed to add gpio device\n");
goto out_error;
}
}
- return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
+ return devm_add_action_or_reset(adp5585->dev, adp5585_remove_devices, adp5585->dev);
out_error:
- mfd_remove_devices(dev);
+ mfd_remove_devices(adp5585->dev);
return ret;
}
@@ -161,19 +251,24 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
if (!adp5585->variant)
return -ENODEV;
- adp5585_fill_regmap_config(adp5585, ®map_config);
+ ret = adp5585_fill_variant_config(adp5585, ®map_config);
+ if (ret)
+ return ret;
adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
if (IS_ERR(adp5585->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
"Failed to initialize register map\n");
+ adp5585->dev = &i2c->dev;
+
ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
if (ret)
return dev_err_probe(&i2c->dev, ret,
"Failed to read device ID\n");
- if ((id & ADP5585_MAN_ID_MASK) != ADP5585_MAN_ID_VALUE)
+ id &= ADP5585_MAN_ID_MASK;
+ if (id != adp5585->id)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
@@ -193,7 +288,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
- return adp5585_add_devices(&i2c->dev);
+ return adp5585_add_devices(adp5585);
}
static int adp5585_suspend(struct device *dev)
@@ -233,6 +328,18 @@ static const struct of_device_id adp5585_of_match[] = {
}, {
.compatible = "adi,adp5585-04",
.data = (void *)ADP5585_04,
+ }, {
+ .compatible = "adi,adp5589-00",
+ .data = (void *)ADP5589_00,
+ }, {
+ .compatible = "adi,adp5589-01",
+ .data = (void *)ADP5589_01,
+ }, {
+ .compatible = "adi,adp5589-02",
+ .data = (void *)ADP5589_02,
+ }, {
+ .compatible = "adi,adp5589",
+ .data = (void *)ADP5589_00,
},
{ /* sentinel */ }
};
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 2813b20e638b6e73ef198e43af07ef29ff25f273..40c87981e5a24f8a175cc41e38b1495ed9f194e5 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -117,6 +117,12 @@
#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
+/* ADP5589 */
+#define ADP5589_MAN_ID_VALUE 0x10
+#define ADP5589_GPI_STATUS_C 0x18
+#define ADP5589_INT_EN 0x4e
+#define ADP5589_MAX_REG ADP5589_INT_EN
+
struct regmap;
enum adp5585_variant {
@@ -125,12 +131,17 @@ enum adp5585_variant {
ADP5585_02,
ADP5585_03,
ADP5585_04,
+ ADP5589_00,
+ ADP5589_01,
+ ADP5589_02,
ADP5585_MAX
};
struct adp5585_dev {
struct regmap *regmap;
+ struct device *dev;
enum adp5585_variant variant;
+ unsigned int id;
};
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 07/20] mfd: adp5585: add support for adp5589
2025-05-21 13:02 ` [PATCH v4 07/20] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-05-23 15:15 ` Lee Jones
2025-05-27 10:20 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-05-23 15:15 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> programmable logic, reset generator, and PWM generator.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 139 +++++++++++++++++++++++++++++++++++++++-----
> include/linux/mfd/adp5585.h | 11 ++++
> 2 files changed, 134 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 672f3468bda5be6af85a5982c3626053b4cb59bf..53ac46365e56874a05855f1df1843717148f181a 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -29,6 +29,11 @@ static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> MFD_CELL_NAME("adp5585-pwm"),
> };
>
> +static const struct mfd_cell adp5589_devs[] = {
> + MFD_CELL_NAME("adp5589-gpio"),
> + MFD_CELL_NAME("adp5589-pwm"),
> +};
> +
> static const struct regmap_range adp5585_volatile_ranges[] = {
> regmap_reg_range(ADP5585_ID, ADP5585_GPI_STATUS_B),
> };
> @@ -38,6 +43,15 @@ static const struct regmap_access_table adp5585_volatile_regs = {
> .n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges),
> };
>
> +static const struct regmap_range adp5589_volatile_ranges[] = {
> + regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C),
> +};
> +
> +static const struct regmap_access_table adp5589_volatile_regs = {
> + .yes_ranges = adp5589_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges),
> +};
> +
> /*
> * Chip variants differ in the default configuration of pull-up and pull-down
> * resistors, and therefore have different default register values:
> @@ -81,6 +95,45 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
> /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
> };
>
> +static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = {
Why +1?
> + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = {
> + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
> + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
> +};
> +
> +static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = {
> + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00,
> + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> /* -1 since the enum starts at 1 for error checking in i2c_get_match_data()*/
> static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
> [ADP5585_00 - 1] = adp5585_regmap_defaults_00,
> @@ -88,6 +141,9 @@ static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
> [ADP5585_02 - 1] = adp5585_regmap_defaults_02,
> [ADP5585_03 - 1] = adp5585_regmap_defaults_00,
> [ADP5585_04 - 1] = adp5585_regmap_defaults_04,
> + [ADP5589_00 - 1] = adp5589_regmap_defaults_00,
> + [ADP5589_01 - 1] = adp5589_regmap_defaults_01,
> + [ADP5589_02 - 1] = adp5589_regmap_defaults_02,
> };
>
> static const struct regmap_config adp5585_regmap_config_template = {
> @@ -99,11 +155,39 @@ static const struct regmap_config adp5585_regmap_config_template = {
> .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
> };
>
> -static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> +static const struct regmap_config adp5589_regmap_config_template = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ADP5589_MAX_REG,
> + .volatile_table = &adp5589_volatile_regs,
> + .cache_type = REGCACHE_MAPLE,
> + .num_reg_defaults_raw = ADP5589_MAX_REG + 1,
This is 0x4F, but there are only 0x4E values?
> +};
> +
> +static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> struct regmap_config *regmap_config)
> {
> - *regmap_config = adp5585_regmap_config_template;
> + switch (adp5585->variant) {
> + case ADP5585_00:
> + case ADP5585_01:
> + case ADP5585_02:
> + case ADP5585_03:
> + case ADP5585_04:
> + *regmap_config = adp5585_regmap_config_template;
> + adp5585->id = ADP5585_MAN_ID_VALUE;
> + break;
> + case ADP5589_00:
> + case ADP5589_01:
> + case ADP5589_02:
> + *regmap_config = adp5589_regmap_config_template;
> + adp5585->id = ADP5589_MAN_ID_VALUE;
> + break;
> + default:
> + return -ENODEV;
If you take my previous advice, this will become ERR_PTR();
> + }
> +
> regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant - 1];
> + return 0;
> }
>
> static void adp5585_remove_devices(void *dev)
> @@ -111,29 +195,35 @@ static void adp5585_remove_devices(void *dev)
> mfd_remove_devices(dev);
> }
>
> -static int adp5585_add_devices(struct device *dev)
> +static int adp5585_add_devices(const struct adp5585_dev *adp5585)
> {
> + const struct mfd_cell *cells;
> int ret;
>
> - if (device_property_present(dev, "#pwm-cells")) {
> - ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> - &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
> + if (adp5585->id == ADP5585_MAN_ID_VALUE)
> + cells = adp5585_devs;
> + else
> + cells = adp5589_devs;
> +
> + if (device_property_present(adp5585->dev, "#pwm-cells")) {
> + ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
Instead of making all of these changes, add the following at the top:
struct device *dev = adp5585->dev;
... and continue using the short form.
> + &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
> if (ret)
> - return dev_err_probe(dev, ret, "Failed to add pwm device\n");
> + return dev_err_probe(adp5585->dev, ret, "Failed to add pwm device\n");
> }
>
> - if (device_property_present(dev, "#gpio-cells")) {
> - ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> - &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
> + if (device_property_present(adp5585->dev, "#gpio-cells")) {
> + ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
> + &cells[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
> if (ret) {
> - ret = dev_err_probe(dev, ret, "Failed to add gpio device\n");
> + ret = dev_err_probe(adp5585->dev, ret, "Failed to add gpio device\n");
> goto out_error;
> }
> }
>
> - return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
> + return devm_add_action_or_reset(adp5585->dev, adp5585_remove_devices, adp5585->dev);
> out_error:
> - mfd_remove_devices(dev);
> + mfd_remove_devices(adp5585->dev);
> return ret;
> }
>
> @@ -161,19 +251,24 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> if (!adp5585->variant)
> return -ENODEV;
>
> - adp5585_fill_regmap_config(adp5585, ®map_config);
> + ret = adp5585_fill_variant_config(adp5585, ®map_config);
> + if (ret)
> + return ret;
>
> adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> "Failed to initialize register map\n");
>
> + adp5585->dev = &i2c->dev;
> +
> ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
> if (ret)
> return dev_err_probe(&i2c->dev, ret,
> "Failed to read device ID\n");
>
> - if ((id & ADP5585_MAN_ID_MASK) != ADP5585_MAN_ID_VALUE)
> + id &= ADP5585_MAN_ID_MASK;
> + if (id != adp5585->id)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> @@ -193,7 +288,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> if (ret)
> return ret;
>
> - return adp5585_add_devices(&i2c->dev);
> + return adp5585_add_devices(adp5585);
> }
>
> static int adp5585_suspend(struct device *dev)
> @@ -233,6 +328,18 @@ static const struct of_device_id adp5585_of_match[] = {
> }, {
> .compatible = "adi,adp5585-04",
> .data = (void *)ADP5585_04,
> + }, {
> + .compatible = "adi,adp5589-00",
> + .data = (void *)ADP5589_00,
> + }, {
> + .compatible = "adi,adp5589-01",
> + .data = (void *)ADP5589_01,
> + }, {
> + .compatible = "adi,adp5589-02",
> + .data = (void *)ADP5589_02,
> + }, {
> + .compatible = "adi,adp5589",
> + .data = (void *)ADP5589_00,
> },
> { /* sentinel */ }
> };
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 2813b20e638b6e73ef198e43af07ef29ff25f273..40c87981e5a24f8a175cc41e38b1495ed9f194e5 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -117,6 +117,12 @@
> #define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
> #define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
>
> +/* ADP5589 */
> +#define ADP5589_MAN_ID_VALUE 0x10
> +#define ADP5589_GPI_STATUS_C 0x18
> +#define ADP5589_INT_EN 0x4e
> +#define ADP5589_MAX_REG ADP5589_INT_EN
> +
> struct regmap;
>
> enum adp5585_variant {
> @@ -125,12 +131,17 @@ enum adp5585_variant {
> ADP5585_02,
> ADP5585_03,
> ADP5585_04,
> + ADP5589_00,
> + ADP5589_01,
> + ADP5589_02,
> ADP5585_MAX
> };
>
> struct adp5585_dev {
> struct regmap *regmap;
> + struct device *dev;
Can you pop this at the top please.
It has a higher standing (in my own head *twitch*) than regmap.
> enum adp5585_variant variant;
> + unsigned int id;
> };
>
> #endif
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 07/20] mfd: adp5585: add support for adp5589
2025-05-23 15:15 ` Lee Jones
@ 2025-05-27 10:20 ` Nuno Sá
0 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá @ 2025-05-27 10:20 UTC (permalink / raw)
To: Lee Jones, nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Fri, 2025-05-23 at 16:15 +0100, Lee Jones wrote:
> On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > programmable logic, reset generator, and PWM generator.
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 139 +++++++++++++++++++++++++++++++++++++++--
> > ---
> > include/linux/mfd/adp5585.h | 11 ++++
> > 2 files changed, 134 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 672f3468bda5be6af85a5982c3626053b4cb59bf..53ac46365e56874a05855f1df184371714
> > 8f181a 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -29,6 +29,11 @@ static const struct mfd_cell
> > adp5585_devs[ADP5585_DEV_MAX] = {
> > MFD_CELL_NAME("adp5585-pwm"),
> > };
> >
> > +static const struct mfd_cell adp5589_devs[] = {
> > + MFD_CELL_NAME("adp5589-gpio"),
> > + MFD_CELL_NAME("adp5589-pwm"),
> > +};
> > +
> > static const struct regmap_range adp5585_volatile_ranges[] = {
> > regmap_reg_range(ADP5585_ID, ADP5585_GPI_STATUS_B),
> > };
> > @@ -38,6 +43,15 @@ static const struct regmap_access_table
> > adp5585_volatile_regs = {
> > .n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges),
> > };
> >
> > +static const struct regmap_range adp5589_volatile_ranges[] = {
> > + regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C),
> > +};
> > +
> > +static const struct regmap_access_table adp5589_volatile_regs = {
> > + .yes_ranges = adp5589_volatile_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges),
> > +};
> > +
> > /*
> > * Chip variants differ in the default configuration of pull-up and pull-
> > down
> > * resistors, and therefore have different default register values:
> > @@ -81,6 +95,45 @@ static const u8
> > adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
> > /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
> > };
> >
> > +static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = {
>
> Why +1?
>
> > + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +};
> > +
> > +static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = {
> > + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
> > + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
> > +};
> > +
> > +static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = {
> > + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00,
> > + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +};
> > +
> > /* -1 since the enum starts at 1 for error checking in
> > i2c_get_match_data()*/
> > static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1] = {
> > [ADP5585_00 - 1] = adp5585_regmap_defaults_00,
> > @@ -88,6 +141,9 @@ static const u8 *adp5585_regmap_defaults[ADP5585_MAX - 1]
> > = {
> > [ADP5585_02 - 1] = adp5585_regmap_defaults_02,
> > [ADP5585_03 - 1] = adp5585_regmap_defaults_00,
> > [ADP5585_04 - 1] = adp5585_regmap_defaults_04,
> > + [ADP5589_00 - 1] = adp5589_regmap_defaults_00,
> > + [ADP5589_01 - 1] = adp5589_regmap_defaults_01,
> > + [ADP5589_02 - 1] = adp5589_regmap_defaults_02,
> > };
> >
> > static const struct regmap_config adp5585_regmap_config_template = {
> > @@ -99,11 +155,39 @@ static const struct regmap_config
> > adp5585_regmap_config_template = {
> > .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
> > };
> >
> > -static void adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > +static const struct regmap_config adp5589_regmap_config_template = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ADP5589_MAX_REG,
> > + .volatile_table = &adp5589_volatile_regs,
> > + .cache_type = REGCACHE_MAPLE,
> > + .num_reg_defaults_raw = ADP5589_MAX_REG + 1,
>
> This is 0x4F, but there are only 0x4E values?
Well, you want to write all registers while i < num_reg_defaults_raw. So, to
actually write in ADP5589_MAX_REG, you need + 1. IOW, if your max register
address is 8 and you want to write it, your array map needs to be able go until
index 8.
- Nuno Sá
> > +};
> > +
> > +static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> > struct regmap_config *regmap_config)
> > {
> > - *regmap_config = adp5585_regmap_config_template;
> > + switch (adp5585->variant) {
> > + case ADP5585_00:
> > + case ADP5585_01:
> > + case ADP5585_02:
> > + case ADP5585_03:
> > + case ADP5585_04:
> > + *regmap_config = adp5585_regmap_config_template;
> > + adp5585->id = ADP5585_MAN_ID_VALUE;
> > + break;
> > + case ADP5589_00:
> > + case ADP5589_01:
> > + case ADP5589_02:
> > + *regmap_config = adp5589_regmap_config_template;
> > + adp5585->id = ADP5589_MAN_ID_VALUE;
> > + break;
> > + default:
> > + return -ENODEV;
>
> If you take my previous advice, this will become ERR_PTR();
>
> > + }
> > +
> > regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585-
> > >variant - 1];
> > + return 0;
> > }
> >
> > static void adp5585_remove_devices(void *dev)
> > @@ -111,29 +195,35 @@ static void adp5585_remove_devices(void *dev)
> > mfd_remove_devices(dev);
> > }
> >
> > -static int adp5585_add_devices(struct device *dev)
> > +static int adp5585_add_devices(const struct adp5585_dev *adp5585)
> > {
> > + const struct mfd_cell *cells;
> > int ret;
> >
> > - if (device_property_present(dev, "#pwm-cells")) {
> > - ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > - &adp5585_devs[ADP5585_DEV_PWM], 1,
> > NULL, 0, NULL);
> > + if (adp5585->id == ADP5585_MAN_ID_VALUE)
> > + cells = adp5585_devs;
> > + else
> > + cells = adp5589_devs;
> > +
> > + if (device_property_present(adp5585->dev, "#pwm-cells")) {
> > + ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
>
> Instead of making all of these changes, add the following at the top:
>
> struct device *dev = adp5585->dev;
>
> ... and continue using the short form.
ack
>
> > + &cells[ADP5585_DEV_PWM], 1, NULL, 0,
> > NULL);
> > if (ret)
> > - return dev_err_probe(dev, ret, "Failed to add pwm
> > device\n");
> > + return dev_err_probe(adp5585->dev, ret, "Failed to
> > add pwm device\n");
> > }
> >
> > - if (device_property_present(dev, "#gpio-cells")) {
> > - ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
> > - &adp5585_devs[ADP5585_DEV_GPIO], 1,
> > NULL, 0, NULL);
> > + if (device_property_present(adp5585->dev, "#gpio-cells")) {
> > + ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
> > + &cells[ADP5585_DEV_GPIO], 1, NULL, 0,
> > NULL);
> > if (ret) {
> > - ret = dev_err_probe(dev, ret, "Failed to add gpio
> > device\n");
> > + ret = dev_err_probe(adp5585->dev, ret, "Failed to
> > add gpio device\n");
> > goto out_error;
> > }
> > }
> >
> > - return devm_add_action_or_reset(dev, adp5585_remove_devices, dev);
> > + return devm_add_action_or_reset(adp5585->dev,
> > adp5585_remove_devices, adp5585->dev);
> > out_error:
> > - mfd_remove_devices(dev);
> > + mfd_remove_devices(adp5585->dev);
> > return ret;
> > }
> >
> > @@ -161,19 +251,24 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > if (!adp5585->variant)
> > return -ENODEV;
> >
> > - adp5585_fill_regmap_config(adp5585, ®map_config);
> > + ret = adp5585_fill_variant_config(adp5585, ®map_config);
> > + if (ret)
> > + return ret;
> >
> > adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> > if (IS_ERR(adp5585->regmap))
> > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > "Failed to initialize register
> > map\n");
> >
> > + adp5585->dev = &i2c->dev;
> > +
> > ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
> > if (ret)
> > return dev_err_probe(&i2c->dev, ret,
> > "Failed to read device ID\n");
> >
> > - if ((id & ADP5585_MAN_ID_MASK) != ADP5585_MAN_ID_VALUE)
> > + id &= ADP5585_MAN_ID_MASK;
> > + if (id != adp5585->id)
> > return dev_err_probe(&i2c->dev, -ENODEV,
> > "Invalid device ID 0x%02x\n", id);
> >
> > @@ -193,7 +288,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > if (ret)
> > return ret;
> >
> > - return adp5585_add_devices(&i2c->dev);
> > + return adp5585_add_devices(adp5585);
> > }
> >
> > static int adp5585_suspend(struct device *dev)
> > @@ -233,6 +328,18 @@ static const struct of_device_id adp5585_of_match[] = {
> > }, {
> > .compatible = "adi,adp5585-04",
> > .data = (void *)ADP5585_04,
> > + }, {
> > + .compatible = "adi,adp5589-00",
> > + .data = (void *)ADP5589_00,
> > + }, {
> > + .compatible = "adi,adp5589-01",
> > + .data = (void *)ADP5589_01,
> > + }, {
> > + .compatible = "adi,adp5589-02",
> > + .data = (void *)ADP5589_02,
> > + }, {
> > + .compatible = "adi,adp5589",
> > + .data = (void *)ADP5589_00,
> > },
> > { /* sentinel */ }
> > };
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index
> > 2813b20e638b6e73ef198e43af07ef29ff25f273..40c87981e5a24f8a175cc41e38b1495ed9
> > f194e5 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -117,6 +117,12 @@
> > #define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
> > #define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
> >
> > +/* ADP5589 */
> > +#define ADP5589_MAN_ID_VALUE 0x10
> > +#define ADP5589_GPI_STATUS_C 0x18
> > +#define ADP5589_INT_EN 0x4e
> > +#define ADP5589_MAX_REG ADP5589_INT_EN
> > +
> > struct regmap;
> >
> > enum adp5585_variant {
> > @@ -125,12 +131,17 @@ enum adp5585_variant {
> > ADP5585_02,
> > ADP5585_03,
> > ADP5585_04,
> > + ADP5589_00,
> > + ADP5589_01,
> > + ADP5589_02,
> > ADP5585_MAX
> > };
> >
> > struct adp5585_dev {
> > struct regmap *regmap;
> > + struct device *dev;
>
> Can you pop this at the top please.
>
> It has a higher standing (in my own head *twitch*) than regmap.
Sure.
>
> > enum adp5585_variant variant;
> > + unsigned int id;
> > };
> >
> > #endif
> >
> > --
> > 2.49.0
> >
> >
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 08/20] mfd: adp5585: add a per chip reg struture
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (6 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 07/20] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-06-12 14:22 ` Lee Jones
2025-05-21 13:02 ` [PATCH v4 09/20] gpio: adp5585: add support for the adp5589 expander Nuno Sá via B4 Relay
` (11 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
There are some differences in the register map between the devices.
Hence, add a register structure per device. This will be needed in
following patches.
On top of that adp5585_fill_regmap_config() is renamed and reworked so
that the current struct adp5585_info act as template (they indeed
contain all the different data between variants) which can then be
complemented depending on the device (as identified by the id register).
This is done like this since a lot of the data is pretty much the same
between variants of the same device.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 10 ++++++++++
include/linux/mfd/adp5585.h | 6 ++++++
2 files changed, 16 insertions(+)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 53ac46365e56874a05855f1df1843717148f181a..e90d16389732f3d8790eb910acd82be2033eaa7e 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -164,6 +164,14 @@ static const struct regmap_config adp5589_regmap_config_template = {
.num_reg_defaults_raw = ADP5589_MAX_REG + 1,
};
+static const struct adp5585_regs adp5585_regs = {
+ .ext_cfg = ADP5585_PIN_CONFIG_C,
+};
+
+static const struct adp5585_regs adp5589_regs = {
+ .ext_cfg = ADP5589_PIN_CONFIG_D,
+};
+
static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
struct regmap_config *regmap_config)
{
@@ -175,12 +183,14 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
case ADP5585_04:
*regmap_config = adp5585_regmap_config_template;
adp5585->id = ADP5585_MAN_ID_VALUE;
+ adp5585->regs = &adp5585_regs;
break;
case ADP5589_00:
case ADP5589_01:
case ADP5589_02:
*regmap_config = adp5589_regmap_config_template;
adp5585->id = ADP5589_MAN_ID_VALUE;
+ adp5585->regs = &adp5589_regs;
break;
default:
return -ENODEV;
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 40c87981e5a24f8a175cc41e38b1495ed9f194e5..a20aa435100aaccb5ed22910933e1856409ba397 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -120,6 +120,7 @@
/* ADP5589 */
#define ADP5589_MAN_ID_VALUE 0x10
#define ADP5589_GPI_STATUS_C 0x18
+#define ADP5589_PIN_CONFIG_D 0x4C
#define ADP5589_INT_EN 0x4e
#define ADP5589_MAX_REG ADP5589_INT_EN
@@ -137,7 +138,12 @@ enum adp5585_variant {
ADP5585_MAX
};
+struct adp5585_regs {
+ unsigned int ext_cfg;
+};
+
struct adp5585_dev {
+ const struct adp5585_regs *regs;
struct regmap *regmap;
struct device *dev;
enum adp5585_variant variant;
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 08/20] mfd: adp5585: add a per chip reg struture
2025-05-21 13:02 ` [PATCH v4 08/20] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
@ 2025-06-12 14:22 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 14:22 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> There are some differences in the register map between the devices.
> Hence, add a register structure per device. This will be needed in
> following patches.
>
> On top of that adp5585_fill_regmap_config() is renamed and reworked so
> that the current struct adp5585_info act as template (they indeed
> contain all the different data between variants) which can then be
> complemented depending on the device (as identified by the id register).
> This is done like this since a lot of the data is pretty much the same
> between variants of the same device.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Reviewed-by: Lee Jones <lee@kernel.org>
> ---
> drivers/mfd/adp5585.c | 10 ++++++++++
> include/linux/mfd/adp5585.h | 6 ++++++
> 2 files changed, 16 insertions(+)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 53ac46365e56874a05855f1df1843717148f181a..e90d16389732f3d8790eb910acd82be2033eaa7e 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -164,6 +164,14 @@ static const struct regmap_config adp5589_regmap_config_template = {
> .num_reg_defaults_raw = ADP5589_MAX_REG + 1,
> };
>
> +static const struct adp5585_regs adp5585_regs = {
> + .ext_cfg = ADP5585_PIN_CONFIG_C,
> +};
> +
> +static const struct adp5585_regs adp5589_regs = {
> + .ext_cfg = ADP5589_PIN_CONFIG_D,
> +};
> +
> static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> struct regmap_config *regmap_config)
> {
> @@ -175,12 +183,14 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> case ADP5585_04:
> *regmap_config = adp5585_regmap_config_template;
> adp5585->id = ADP5585_MAN_ID_VALUE;
> + adp5585->regs = &adp5585_regs;
> break;
> case ADP5589_00:
> case ADP5589_01:
> case ADP5589_02:
> *regmap_config = adp5589_regmap_config_template;
> adp5585->id = ADP5589_MAN_ID_VALUE;
> + adp5585->regs = &adp5589_regs;
> break;
> default:
> return -ENODEV;
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 40c87981e5a24f8a175cc41e38b1495ed9f194e5..a20aa435100aaccb5ed22910933e1856409ba397 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -120,6 +120,7 @@
> /* ADP5589 */
> #define ADP5589_MAN_ID_VALUE 0x10
> #define ADP5589_GPI_STATUS_C 0x18
> +#define ADP5589_PIN_CONFIG_D 0x4C
> #define ADP5589_INT_EN 0x4e
> #define ADP5589_MAX_REG ADP5589_INT_EN
>
> @@ -137,7 +138,12 @@ enum adp5585_variant {
> ADP5585_MAX
> };
>
> +struct adp5585_regs {
> + unsigned int ext_cfg;
> +};
> +
> struct adp5585_dev {
> + const struct adp5585_regs *regs;
> struct regmap *regmap;
> struct device *dev;
> enum adp5585_variant variant;
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 09/20] gpio: adp5585: add support for the adp5589 expander
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (7 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 08/20] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
@ 2025-05-21 13:02 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 10/20] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
` (10 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:02 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Support the adp5589 I/O expander which supports up to 19 pins. We need
to add a chip_info based struct since accessing register "banks"
and "bits" differs between devices.
Also some register addresses are different.
While at it move ADP558X_GPIO_MAX defines to the main header file and
rename them. That information will be needed by the top level device in
a following change.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/gpio/gpio-adp5585.c | 151 ++++++++++++++++++++++++++++++++++----------
include/linux/mfd/adp5585.h | 18 +++---
2 files changed, 126 insertions(+), 43 deletions(-)
diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
index d5c0f1b267c82a5002b50cbb7a108166439e4785..cdf107742579cb44d73cc030646358ba5a23fd97 100644
--- a/drivers/gpio/gpio-adp5585.c
+++ b/drivers/gpio/gpio-adp5585.c
@@ -4,6 +4,7 @@
*
* Copyright 2022 NXP
* Copyright 2024 Ideas on Board Oy
+ * Copyright 2025 Analog Devices, Inc.
*/
#include <linux/device.h>
@@ -14,57 +15,106 @@
#include <linux/regmap.h>
#include <linux/types.h>
-#define ADP5585_GPIO_MAX 11
+/*
+ * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the
+ * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to
+ * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver
+ * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is
+ * marked as reserved in the device tree for variants that don't support it.
+ */
+#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
+#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
+
+/*
+ * Bank 0 covers pins "GPIO 1/R0" to "GPIO 8/R7", numbered 0 to 7 by the
+ * driver, bank 1 covers pins "GPIO 9/C0" to "GPIO 16/C7", numbered 8 to
+ * 15 and bank 3 covers pins "GPIO 17/C8" to "GPIO 19/C10", numbered 16 to 18.
+ */
+#define ADP5589_BANK(n) ((n) >> 3)
+#define ADP5589_BIT(n) BIT((n) & 0x7)
+
+struct adp5585_gpio_chip {
+ int (*bank)(unsigned int off);
+ int (*bit)(unsigned int off);
+ unsigned int max_gpio;
+ unsigned int debounce_dis_a;
+ unsigned int rpull_cfg_a;
+ unsigned int gpo_data_a;
+ unsigned int gpo_out_a;
+ unsigned int gpio_dir_a;
+ unsigned int gpi_stat_a;
+ bool has_bias_hole;
+};
struct adp5585_gpio_dev {
struct gpio_chip gpio_chip;
+ const struct adp5585_gpio_chip *info;
struct regmap *regmap;
};
+static int adp5585_gpio_bank(unsigned int off)
+{
+ return ADP5585_BANK(off);
+}
+
+static int adp5585_gpio_bit(unsigned int off)
+{
+ return ADP5585_BIT(off);
+}
+
+static int adp5589_gpio_bank(unsigned int off)
+{
+ return ADP5589_BANK(off);
+}
+
+static int adp5589_gpio_bit(unsigned int off)
+{
+ return ADP5589_BIT(off);
+}
+
static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
{
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
unsigned int val;
- regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
+ regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), &val);
- return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+ return val & info->bit(off) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
{
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
- return regmap_clear_bits(adp5585_gpio->regmap,
- ADP5585_GPIO_DIRECTION_A + bank, bit);
+ return regmap_clear_bits(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off),
+ info->bit(off));
}
static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val)
{
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ unsigned int bank = info->bank(off);
+ unsigned int bit = info->bit(off);
int ret;
- ret = regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_GPO_DATA_OUT_A + bank, bit,
- val ? bit : 0);
+ ret = regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + bank,
+ bit, val ? bit : 0);
if (ret)
return ret;
- return regmap_set_bits(adp5585_gpio->regmap,
- ADP5585_GPIO_DIRECTION_A + bank, bit);
+ return regmap_set_bits(adp5585_gpio->regmap, info->gpio_dir_a + bank,
+ bit);
}
static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
{
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ unsigned int bank = info->bank(off);
+ unsigned int bit = info->bit(off);
unsigned int reg;
unsigned int val;
@@ -79,8 +129,8 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
* .direction_input(), .direction_output() or .set() operations racing
* with this.
*/
- regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
- reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A;
+ regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + bank, &val);
+ reg = val & bit ? info->gpo_data_a : info->gpi_stat_a;
regmap_read(adp5585_gpio->regmap, reg + bank, &val);
return !!(val & bit);
@@ -90,17 +140,17 @@ static int adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off,
int val)
{
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ unsigned int bit = adp5585_gpio->info->bit(off);
- return regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_GPO_DATA_OUT_A + bank,
+ return regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + info->bank(off),
bit, val ? bit : 0);
}
static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
unsigned int off, unsigned int bias)
{
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
unsigned int bit, reg, mask, val;
/*
@@ -108,8 +158,10 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
* consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits
* after R5.
*/
- bit = off * 2 + (off > 5 ? 4 : 0);
- reg = ADP5585_RPULL_CONFIG_A + bit / 8;
+ bit = off * 2;
+ if (info->has_bias_hole)
+ bit += (off > 5 ? 4 : 0);
+ reg = info->rpull_cfg_a + bit / 8;
mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
val = bias << (bit % 8);
@@ -119,22 +171,22 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio,
unsigned int off, enum pin_config_param drive)
{
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ unsigned int bit = adp5585_gpio->info->bit(off);
return regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_GPO_OUT_MODE_A + bank, bit,
+ info->gpo_out_a + info->bank(off), bit,
drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0);
}
static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio,
unsigned int off, unsigned int debounce)
{
- unsigned int bank = ADP5585_BANK(off);
- unsigned int bit = ADP5585_BIT(off);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ unsigned int bit = adp5585_gpio->info->bit(off);
return regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_DEBOUNCE_DIS_A + bank, bit,
+ info->debounce_dis_a + info->bank(off), bit,
debounce ? 0 : bit);
}
@@ -175,6 +227,7 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
static int adp5585_gpio_probe(struct platform_device *pdev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
+ const struct platform_device_id *id = platform_get_device_id(pdev);
struct adp5585_gpio_dev *adp5585_gpio;
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
@@ -186,6 +239,10 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
adp5585_gpio->regmap = adp5585->regmap;
+ adp5585_gpio->info = (const struct adp5585_gpio_chip *)id->driver_data;
+ if (!adp5585_gpio->info)
+ return -ENODEV;
+
device_set_of_node_from_dev(dev, dev->parent);
gc = &adp5585_gpio->gpio_chip;
@@ -199,7 +256,7 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
gc->can_sleep = true;
gc->base = -1;
- gc->ngpio = ADP5585_GPIO_MAX;
+ gc->ngpio = adp5585_gpio->info->max_gpio;
gc->label = pdev->name;
gc->owner = THIS_MODULE;
@@ -211,8 +268,34 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
return 0;
}
+static const struct adp5585_gpio_chip adp5585_gpio_chip_info = {
+ .bank = adp5585_gpio_bank,
+ .bit = adp5585_gpio_bit,
+ .debounce_dis_a = ADP5585_DEBOUNCE_DIS_A,
+ .rpull_cfg_a = ADP5585_RPULL_CONFIG_A,
+ .gpo_data_a = ADP5585_GPO_DATA_OUT_A,
+ .gpo_out_a = ADP5585_GPO_OUT_MODE_A,
+ .gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
+ .gpi_stat_a = ADP5585_GPI_STATUS_A,
+ .max_gpio = ADP5585_PIN_MAX,
+ .has_bias_hole = true,
+};
+
+static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
+ .bank = adp5589_gpio_bank,
+ .bit = adp5589_gpio_bit,
+ .debounce_dis_a = ADP5589_DEBOUNCE_DIS_A,
+ .rpull_cfg_a = ADP5589_RPULL_CONFIG_A,
+ .gpo_data_a = ADP5589_GPO_DATA_OUT_A,
+ .gpo_out_a = ADP5589_GPO_OUT_MODE_A,
+ .gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
+ .gpi_stat_a = ADP5589_GPI_STATUS_A,
+ .max_gpio = ADP5589_PIN_MAX,
+};
+
static const struct platform_device_id adp5585_gpio_id_table[] = {
- { "adp5585-gpio" },
+ { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info },
+ { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index a20aa435100aaccb5ed22910933e1856409ba397..df352e39bfd62a17c39b52b0291b18ac4d047d69 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -107,23 +107,23 @@
#define ADP5585_MAX_REG ADP5585_INT_EN
-/*
- * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the
- * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to
- * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver
- * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is
- * marked as reserved in the device tree for variants that don't support it.
- */
-#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
-#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
+#define ADP5585_PIN_MAX 11
/* ADP5589 */
#define ADP5589_MAN_ID_VALUE 0x10
+#define ADP5589_GPI_STATUS_A 0x16
#define ADP5589_GPI_STATUS_C 0x18
+#define ADP5589_RPULL_CONFIG_A 0x19
+#define ADP5589_DEBOUNCE_DIS_A 0x27
+#define ADP5589_GPO_DATA_OUT_A 0x2a
+#define ADP5589_GPO_OUT_MODE_A 0x2d
+#define ADP5589_GPIO_DIRECTION_A 0x30
#define ADP5589_PIN_CONFIG_D 0x4C
#define ADP5589_INT_EN 0x4e
#define ADP5589_MAX_REG ADP5589_INT_EN
+#define ADP5589_PIN_MAX 19
+
struct regmap;
enum adp5585_variant {
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 10/20] pwm: adp5585: add support for adp5589
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (8 preceding siblings ...)
2025-05-21 13:02 ` [PATCH v4 09/20] gpio: adp5585: add support for the adp5589 expander Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 11/20] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
` (9 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Add support for the adp5589 I/O expander. From a PWM point of view it is
pretty similar to adp5585. Main difference is the address
of registers meaningful for configuring the PWM.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/pwm/pwm-adp5585.c | 73 ++++++++++++++++++++++++++++++++++-----------
include/linux/mfd/adp5585.h | 3 ++
2 files changed, 59 insertions(+), 17 deletions(-)
diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
index c8821035b7c1412a55a642e6e8a46b66e693a5af..58f83f16134c0bb66f5a20c041622bf4e3905668 100644
--- a/drivers/pwm/pwm-adp5585.c
+++ b/drivers/pwm/pwm-adp5585.c
@@ -32,21 +32,33 @@
#define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
#define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
+struct adp5585_pwm_chip {
+ unsigned int pwm_cfg;
+ unsigned int pwm_offt_low;
+ unsigned int pwm_ont_low;
+};
+
+struct adp5585_pwm {
+ const struct adp5585_pwm_chip *info;
+ struct regmap *regmap;
+ unsigned int ext_cfg;
+};
+
static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
- struct regmap *regmap = pwmchip_get_drvdata(chip);
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
/* Configure the R3 pin as PWM output. */
- return regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
+ return regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
ADP5585_R3_EXTEND_CFG_MASK,
ADP5585_R3_EXTEND_CFG_PWM_OUT);
}
static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
- struct regmap *regmap = pwmchip_get_drvdata(chip);
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
- regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
+ regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
ADP5585_R3_EXTEND_CFG_MASK,
ADP5585_R3_EXTEND_CFG_GPIO4);
}
@@ -55,14 +67,16 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
struct pwm_device *pwm,
const struct pwm_state *state)
{
- struct regmap *regmap = pwmchip_get_drvdata(chip);
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+ const struct adp5585_pwm_chip *info = adp5585_pwm->info;
+ struct regmap *regmap = adp5585_pwm->regmap;
u64 period, duty_cycle;
u32 on, off;
__le16 val;
int ret;
if (!state->enabled) {
- regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
+ regmap_clear_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
return 0;
}
@@ -83,41 +97,43 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on;
val = cpu_to_le16(off);
- ret = regmap_bulk_write(regmap, ADP5585_PWM_OFFT_LOW, &val, 2);
+ ret = regmap_bulk_write(regmap, info->pwm_offt_low, &val, 2);
if (ret)
return ret;
val = cpu_to_le16(on);
- ret = regmap_bulk_write(regmap, ADP5585_PWM_ONT_LOW, &val, 2);
+ ret = regmap_bulk_write(regmap, info->pwm_ont_low, &val, 2);
if (ret)
return ret;
/* Enable PWM in continuous mode and no external AND'ing. */
- ret = regmap_update_bits(regmap, ADP5585_PWM_CFG,
+ ret = regmap_update_bits(regmap, info->pwm_cfg,
ADP5585_PWM_IN_AND | ADP5585_PWM_MODE |
ADP5585_PWM_EN, ADP5585_PWM_EN);
if (ret)
return ret;
- return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
+ return regmap_set_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
}
static int pwm_adp5585_get_state(struct pwm_chip *chip,
struct pwm_device *pwm,
struct pwm_state *state)
{
- struct regmap *regmap = pwmchip_get_drvdata(chip);
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+ const struct adp5585_pwm_chip *info = adp5585_pwm->info;
+ struct regmap *regmap = adp5585_pwm->regmap;
unsigned int on, off;
unsigned int val;
__le16 on_off;
int ret;
- ret = regmap_bulk_read(regmap, ADP5585_PWM_OFFT_LOW, &on_off, 2);
+ ret = regmap_bulk_read(regmap, info->pwm_offt_low, &on_off, 2);
if (ret)
return ret;
off = le16_to_cpu(on_off);
- ret = regmap_bulk_read(regmap, ADP5585_PWM_ONT_LOW, &on_off, 2);
+ ret = regmap_bulk_read(regmap, info->pwm_ont_low, &on_off, 2);
if (ret)
return ret;
on = le16_to_cpu(on_off);
@@ -127,7 +143,7 @@ static int pwm_adp5585_get_state(struct pwm_chip *chip,
state->polarity = PWM_POLARITY_NORMAL;
- regmap_read(regmap, ADP5585_PWM_CFG, &val);
+ regmap_read(regmap, info->pwm_cfg, &val);
state->enabled = !!(val & ADP5585_PWM_EN);
return 0;
@@ -142,18 +158,28 @@ static const struct pwm_ops adp5585_pwm_ops = {
static int adp5585_pwm_probe(struct platform_device *pdev)
{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
struct device *dev = &pdev->dev;
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+ struct adp5585_pwm *adp5585_pwm;
struct pwm_chip *chip;
int ret;
- chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM, 0);
+ chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM,
+ sizeof(*adp5585_pwm));
if (IS_ERR(chip))
return PTR_ERR(chip);
+ adp5585_pwm = pwmchip_get_drvdata(chip);
+ adp5585_pwm->regmap = adp5585->regmap;
+ adp5585_pwm->ext_cfg = adp5585->regs->ext_cfg;
+
+ adp5585_pwm->info = (const struct adp5585_pwm_chip *)id->driver_data;
+ if (!adp5585_pwm->info)
+ return -ENODEV;
+
device_set_of_node_from_dev(dev, dev->parent);
- pwmchip_set_drvdata(chip, adp5585->regmap);
chip->ops = &adp5585_pwm_ops;
ret = devm_pwmchip_add(dev, chip);
@@ -163,8 +189,21 @@ static int adp5585_pwm_probe(struct platform_device *pdev)
return 0;
}
+static const struct adp5585_pwm_chip adp5589_pwm_chip_info = {
+ .pwm_cfg = ADP5585_PWM_CFG,
+ .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5585_PWM_ONT_LOW,
+};
+
+static const struct adp5585_pwm_chip adp5585_pwm_chip_info = {
+ .pwm_cfg = ADP5589_PWM_CFG,
+ .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5589_PWM_ONT_LOW,
+};
+
static const struct platform_device_id adp5585_pwm_id_table[] = {
- { "adp5585-pwm" },
+ { "adp5585-pwm", (kernel_ulong_t)&adp5585_pwm_chip_info },
+ { "adp5589-pwm", (kernel_ulong_t)&adp5589_pwm_chip_info },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index df352e39bfd62a17c39b52b0291b18ac4d047d69..2a2bccccaa6ca5cba8ff5716c0d3b82d9541432c 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -118,6 +118,9 @@
#define ADP5589_GPO_DATA_OUT_A 0x2a
#define ADP5589_GPO_OUT_MODE_A 0x2d
#define ADP5589_GPIO_DIRECTION_A 0x30
+#define ADP5589_PWM_OFFT_LOW 0x3e
+#define ADP5589_PWM_ONT_LOW 0x40
+#define ADP5589_PWM_CFG 0x42
#define ADP5589_PIN_CONFIG_D 0x4C
#define ADP5589_INT_EN 0x4e
#define ADP5589_MAX_REG ADP5589_INT_EN
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 11/20] dt-bindings: mfd: adp5585: add properties for input events
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (9 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 10/20] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 12/20] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
` (8 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Add properties related to input events. These devices can act as
keyboards and can support events either via a keymap Matrix or through
GPIs. Note that the device needs to be an interrupt controller for GPIs
based events.
We specifically need a property specifying the pins used by the keymap
matrix since these devices have no requirement for rows and columns to be
contiguous without holes which is enforced by the standard input
properties.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
.../devicetree/bindings/mfd/adi,adp5585.yaml | 188 ++++++++++++++++++++-
1 file changed, 186 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
index 9471af28419d820424745315ffb2129f7dd37581..b3bf2ed586104303fd078bd06683e4f0d3383575 100644
--- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
+++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
@@ -49,6 +49,84 @@ properties:
"#pwm-cells":
const: 3
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 2
+
+ poll-interval:
+ enum: [10, 20, 30, 40]
+ default: 10
+
+ adi,keypad-pins:
+ description: Specifies the pins used for the keypad matrix.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ adi,unlock-events:
+ description:
+ Specifies a maximum of 2 events that can be used to unlock the keypad.
+ If this property is set, the keyboard will be locked and only unlocked
+ after these keys/gpis are pressed. The value 127 serves as a wildcard which
+ means any key can be used for unlocking.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 2
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 88
+ - minimum: 97
+ maximum: 115
+ - const: 127
+
+ adi,unlock-trigger-sec:
+ description:
+ Defines the time in which the second unlock event must occur after the
+ first unlock event has occurred.
+ maximum: 7
+ default: 0
+
+ adi,reset1-events:
+ description:
+ Defines the trigger events (key/gpi presses) that can generate reset
+ conditions one the reset1 block.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 3
+
+ adi,reset2-events:
+ description:
+ Defines the trigger events (key/gpi presses) that can generate reset
+ conditions one the reset2 block.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 2
+
+ adi,reset1-active-high:
+ description: Sets the reset1 signal as active high.
+ type: boolean
+
+ adi,reset2-active-high:
+ description: Sets the reset2 signal as active high.
+ type: boolean
+
+ adi,rst-passthrough-enable:
+ description: Allows the RST pin to override (OR with) the reset1 signal.
+ type: boolean
+
+ adi,reset-trigger-ms:
+ description:
+ Defines the length of time that the reset events must be active before a
+ reset signal is generated. All events must be active at the same time for
+ the same duration.
+ enum: [0, 1000, 1500, 2000, 2500, 3000, 3500, 4000]
+ default: 0
+
+ adi,reset-pulse-width-us:
+ description: Defines the pulse width of the reset signals.
+ enum: [500, 1000, 2000, 10000]
+ default: 500
+
patternProperties:
"-hog(-[0-9]+)?$":
type: object
@@ -56,11 +134,28 @@ patternProperties:
required:
- gpio-hog
+dependencies:
+ linux,keymap:
+ - adi,keypad-pins
+ - interrupts
+ interrupt-controller:
+ - interrupts
+ adi,unlock-trigger-sec:
+ - adi,unlock-events
+ adi,reset1-active-high:
+ - adi,reset1-events
+ adi,rst-passtrough-enable:
+ - adi,reset1-events
+ adi,reset2-active-high:
+ - adi,reset2-events
+
required:
- compatible
- reg
allOf:
+ - $ref: /schemas/input/matrix-keymap.yaml#
+ - $ref: /schemas/input/input.yaml#
- if:
properties:
compatible:
@@ -68,8 +163,29 @@ allOf:
const: adi,adp5585-01
then:
properties:
+ adi,unlock-events: false
+ adi,unlock-trigger-sec: false
gpio-reserved-ranges: false
-
+ adi,keypad-pins:
+ minItems: 2
+ maxItems: 11
+ items:
+ minimum: 0
+ maximum: 10
+ adi,reset1-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 30
+ - minimum: 37
+ maximum: 47
+ adi,reset2-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 30
+ - minimum: 37
+ maximum: 47
- if:
properties:
compatible:
@@ -81,6 +197,25 @@ allOf:
- adi,adp5585-04
then:
properties:
+ adi,unlock-events: false
+ adi,unlock-trigger-sec: false
+ adi,keypad-pins:
+ minItems: 2
+ maxItems: 10
+ items:
+ enum: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10]
+ adi,reset1-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 25
+ - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
+ adi,reset2-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 25
+ - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
gpio-reserved-ranges:
maxItems: 1
items:
@@ -99,11 +234,33 @@ allOf:
then:
properties:
gpio-reserved-ranges: false
+ adi,keypad-pins:
+ minItems: 2
+ maxItems: 19
+ items:
+ minimum: 0
+ maximum: 18
+ adi,reset1-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 88
+ - minimum: 97
+ maximum: 115
+ adi,reset2-events:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 88
+ - minimum: 97
+ maximum: 115
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
+ #include <dt-bindings/input/input.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -119,6 +276,33 @@ examples:
gpio-reserved-ranges = <5 1>;
#pwm-cells = <3>;
+
+ interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-parent = <&gpio>;
+
+ adi,reset1-events = <1 43>;
+ adi,reset2-events = <2 3>;
+ adi,reset-trigger-ms = <2000>;
+
+ /*
+ * col0, col1, col2
+ * row0, row1, row2
+ */
+ adi,keypad-pins = <0 1 2 6 7 8>;
+
+ linux,keymap = <
+ MATRIX_KEY(0x00, 0x00, KEY_1)
+ MATRIX_KEY(0x00, 0x01, KEY_2)
+ MATRIX_KEY(0x00, 0x02, KEY_3)
+
+ MATRIX_KEY(0x01, 0x00, KEY_A)
+ MATRIX_KEY(0x01, 0x01, KEY_B)
+ MATRIX_KEY(0x01, 0x02, KEY_C)
+
+ MATRIX_KEY(0x02, 0x00, BTN_1)
+ MATRIX_KEY(0x02, 0x01, BTN_2)
+ MATRIX_KEY(0x02, 0x02, BTN_3)
+ >;
};
};
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 12/20] mfd: adp5585: add support for event handling
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (10 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 11/20] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-06-12 14:29 ` Lee Jones
2025-05-21 13:03 ` [PATCH v4 13/20] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
` (7 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
These devices are capable of generate FIFO based events based on KEY or
GPI presses. Add support for handling these events. This is in
preparation of adding full support for keymap and gpis based events.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 184 +++++++++++++++++++++++++++++++++++++++++---
include/linux/mfd/adp5585.h | 18 +++++
2 files changed, 190 insertions(+), 12 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index e90d16389732f3d8790eb910acd82be2033eaa7e..dcc09c898dd7990b39e21cb2324fa66ae171a802 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -8,6 +8,7 @@
*/
#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
@@ -166,10 +167,16 @@ static const struct regmap_config adp5589_regmap_config_template = {
static const struct adp5585_regs adp5585_regs = {
.ext_cfg = ADP5585_PIN_CONFIG_C,
+ .int_en = ADP5585_INT_EN,
+ .gen_cfg = ADP5585_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
};
static const struct adp5585_regs adp5589_regs = {
.ext_cfg = ADP5589_PIN_CONFIG_D,
+ .int_en = ADP5589_INT_EN,
+ .gen_cfg = ADP5589_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
};
static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
@@ -244,6 +251,150 @@ static void adp5585_osc_disable(void *data)
regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
}
+static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
+{
+ unsigned int i;
+
+ for (i = 0; i < ev_cnt; i++) {
+ unsigned long key_val, key_press;
+ unsigned int key;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key);
+ if (ret)
+ return;
+
+ key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key);
+ key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key);
+
+ blocking_notifier_call_chain(&adp5585->event_notifier, key_val, (void *)key_press);
+ }
+}
+
+static irqreturn_t adp5585_irq(int irq, void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+ unsigned int status, ev_cnt;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (status & ADP5585_OVRFLOW_INT)
+ dev_err_ratelimited(adp5585->dev, "Event overflow error\n");
+
+ if (!(status & ADP5585_EVENT_INT))
+ goto out_irq;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
+ if (ret)
+ goto out_irq;
+
+ ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
+ if (!ev_cnt)
+ goto out_irq;
+
+ adp5585_report_events(adp5585, ev_cnt);
+out_irq:
+ regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
+ return IRQ_HANDLED;
+}
+
+static int adp5585_setup(struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->regs;
+ unsigned int reg_val, i;
+ int ret;
+
+ /* Clear any possible event by reading all the FIFO entries */
+ for (i = 0; i < ADP5585_EV_MAX; i++) {
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg, adp5585->ev_poll_time);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable the internal oscillator, as it's shared between multiple
+ * functions.
+ *
+ * As a future improvement, power consumption could possibly be
+ * decreased in some use cases by enabling and disabling the oscillator
+ * dynamically based on the needs of the child drivers.
+ */
+ ret = regmap_write(adp5585->regmap, regs->gen_cfg,
+ ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG | ADP5585_OSC_EN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable, adp5585);
+}
+
+static int adp5585_parse_fw(struct adp5585_dev *adp5585)
+{
+ unsigned int prop_val;
+ int ret;
+
+ ret = device_property_read_u32(adp5585->dev, "poll-interval", &prop_val);
+ if (!ret) {
+ adp5585->ev_poll_time = prop_val / 10 - 1;
+ /*
+ * ev_poll_time is the raw value to be written on the register and 0 to 3 are the
+ * valid values.
+ */
+ if (adp5585->ev_poll_time > 3)
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid value(%u) for poll-interval\n", prop_val);
+ }
+
+ return 0;
+}
+
+static void adp5585_irq_disable(void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+
+ regmap_write(adp5585->regmap, adp5585->regs->int_en, 0);
+}
+
+static int adp5585_irq_enable(struct i2c_client *i2c,
+ struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->regs;
+ unsigned int stat;
+ int ret;
+
+ if (i2c->irq <= 0)
+ return 0;
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq,
+ IRQF_ONESHOT, i2c->name, adp5585);
+ if (ret)
+ return ret;
+
+ /*
+ * Clear any possible outstanding interrupt before enabling them. We do that by reading
+ * the status register and writing back the same value.
+ */
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, regs->int_en, ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable, adp5585);
+}
+
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config regmap_config;
@@ -271,6 +422,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
"Failed to initialize register map\n");
adp5585->dev = &i2c->dev;
+ adp5585->irq = i2c->irq;
+ BLOCKING_INIT_NOTIFIER_HEAD(&adp5585->event_notifier);
ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
if (ret)
@@ -282,29 +435,28 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
- /*
- * Enable the internal oscillator, as it's shared between multiple
- * functions.
- *
- * As a future improvement, power consumption could possibly be
- * decreased in some use cases by enabling and disabling the oscillator
- * dynamically based on the needs of the child drivers.
- */
- ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
+ ret = adp5585_parse_fw(adp5585);
if (ret)
return ret;
- ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
+ ret = adp5585_setup(adp5585);
if (ret)
return ret;
- return adp5585_add_devices(adp5585);
+ ret = adp5585_add_devices(adp5585);
+ if (ret)
+ return ret;
+
+ return adp5585_irq_enable(i2c, adp5585);
}
static int adp5585_suspend(struct device *dev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+ if (adp5585->irq)
+ disable_irq(adp5585->irq);
+
regcache_cache_only(adp5585->regmap, true);
return 0;
@@ -313,11 +465,19 @@ static int adp5585_suspend(struct device *dev)
static int adp5585_resume(struct device *dev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+ int ret;
regcache_cache_only(adp5585->regmap, false);
regcache_mark_dirty(adp5585->regmap);
- return regcache_sync(adp5585->regmap);
+ ret = regcache_sync(adp5585->regmap);
+ if (ret)
+ return ret;
+
+ if (adp5585->irq)
+ enable_irq(adp5585->irq);
+
+ return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 2a2bccccaa6ca5cba8ff5716c0d3b82d9541432c..b6baf87907a567fe975f8b24f3c36753e6145066 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -10,13 +10,20 @@
#define __MFD_ADP5585_H_
#include <linux/bits.h>
+#include <linux/notifier.h>
#define ADP5585_ID 0x00
#define ADP5585_MAN_ID_VALUE 0x20
#define ADP5585_MAN_ID_MASK GENMASK(7, 4)
+#define ADP5585_REV_ID_MASK GENMASK(3, 0)
#define ADP5585_INT_STATUS 0x01
+#define ADP5585_OVRFLOW_INT BIT(2)
+#define ADP5585_EVENT_INT BIT(0)
#define ADP5585_STATUS 0x02
+#define ADP5585_EC_MASK GENMASK(4, 0)
#define ADP5585_FIFO_1 0x03
+#define ADP5585_KEV_EV_PRESS_MASK BIT(7)
+#define ADP5585_KEY_EVENT_MASK GENMASK(6, 0)
#define ADP5585_FIFO_2 0x04
#define ADP5585_FIFO_3 0x05
#define ADP5585_FIFO_4 0x06
@@ -32,6 +39,7 @@
#define ADP5585_FIFO_14 0x10
#define ADP5585_FIFO_15 0x11
#define ADP5585_FIFO_16 0x12
+#define ADP5585_EV_MAX (ADP5585_FIFO_16 - ADP5585_FIFO_1 + 1)
#define ADP5585_GPI_INT_STAT_A 0x13
#define ADP5585_GPI_INT_STAT_B 0x14
#define ADP5585_GPI_STATUS_A 0x15
@@ -104,6 +112,8 @@
#define ADP5585_INT_CFG BIT(1)
#define ADP5585_RST_CFG BIT(0)
#define ADP5585_INT_EN 0x3c
+#define ADP5585_OVRFLOW_IEN BIT(2)
+#define ADP5585_EVENT_IEN BIT(0)
#define ADP5585_MAX_REG ADP5585_INT_EN
@@ -121,7 +131,9 @@
#define ADP5589_PWM_OFFT_LOW 0x3e
#define ADP5589_PWM_ONT_LOW 0x40
#define ADP5589_PWM_CFG 0x42
+#define ADP5589_POLL_PTIME_CFG 0x48
#define ADP5589_PIN_CONFIG_D 0x4C
+#define ADP5589_GENERAL_CFG 0x4d
#define ADP5589_INT_EN 0x4e
#define ADP5589_MAX_REG ADP5589_INT_EN
@@ -142,15 +154,21 @@ enum adp5585_variant {
};
struct adp5585_regs {
+ unsigned int gen_cfg;
unsigned int ext_cfg;
+ unsigned int int_en;
+ unsigned int poll_ptime_cfg;
};
struct adp5585_dev {
const struct adp5585_regs *regs;
struct regmap *regmap;
struct device *dev;
+ struct blocking_notifier_head event_notifier;
enum adp5585_variant variant;
unsigned int id;
+ int irq;
+ unsigned int ev_poll_time;
};
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 12/20] mfd: adp5585: add support for event handling
2025-05-21 13:03 ` [PATCH v4 12/20] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
@ 2025-06-12 14:29 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 14:29 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> These devices are capable of generate FIFO based events based on KEY or
> GPI presses. Add support for handling these events. This is in
> preparation of adding full support for keymap and gpis based events.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 184 +++++++++++++++++++++++++++++++++++++++++---
> include/linux/mfd/adp5585.h | 18 +++++
> 2 files changed, 190 insertions(+), 12 deletions(-)
Reviewed-by: Lee Jones <lee@kernel.org>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index e90d16389732f3d8790eb910acd82be2033eaa7e..dcc09c898dd7990b39e21cb2324fa66ae171a802 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -8,6 +8,7 @@
> */
>
> #include <linux/array_size.h>
> +#include <linux/bitfield.h>
> #include <linux/device.h>
> #include <linux/err.h>
> #include <linux/i2c.h>
> @@ -166,10 +167,16 @@ static const struct regmap_config adp5589_regmap_config_template = {
>
> static const struct adp5585_regs adp5585_regs = {
> .ext_cfg = ADP5585_PIN_CONFIG_C,
> + .int_en = ADP5585_INT_EN,
> + .gen_cfg = ADP5585_GENERAL_CFG,
> + .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> };
>
> static const struct adp5585_regs adp5589_regs = {
> .ext_cfg = ADP5589_PIN_CONFIG_D,
> + .int_en = ADP5589_INT_EN,
> + .gen_cfg = ADP5589_GENERAL_CFG,
> + .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> };
>
> static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> @@ -244,6 +251,150 @@ static void adp5585_osc_disable(void *data)
> regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> }
>
> +static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ev_cnt; i++) {
> + unsigned long key_val, key_press;
> + unsigned int key;
> + int ret;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key);
> + if (ret)
> + return;
> +
> + key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key);
> + key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key);
> +
> + blocking_notifier_call_chain(&adp5585->event_notifier, key_val, (void *)key_press);
> + }
> +}
> +
> +static irqreturn_t adp5585_irq(int irq, void *data)
> +{
> + struct adp5585_dev *adp5585 = data;
> + unsigned int status, ev_cnt;
> + int ret;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
> + if (ret)
> + return IRQ_HANDLED;
> +
> + if (status & ADP5585_OVRFLOW_INT)
> + dev_err_ratelimited(adp5585->dev, "Event overflow error\n");
> +
> + if (!(status & ADP5585_EVENT_INT))
> + goto out_irq;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
> + if (ret)
> + goto out_irq;
> +
> + ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
> + if (!ev_cnt)
> + goto out_irq;
> +
> + adp5585_report_events(adp5585, ev_cnt);
> +out_irq:
> + regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
> + return IRQ_HANDLED;
> +}
> +
> +static int adp5585_setup(struct adp5585_dev *adp5585)
> +{
> + const struct adp5585_regs *regs = adp5585->regs;
> + unsigned int reg_val, i;
> + int ret;
> +
> + /* Clear any possible event by reading all the FIFO entries */
> + for (i = 0; i < ADP5585_EV_MAX; i++) {
> + ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
> + if (ret)
> + return ret;
> + }
> +
> + ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg, adp5585->ev_poll_time);
> + if (ret)
> + return ret;
> +
> + /*
> + * Enable the internal oscillator, as it's shared between multiple
> + * functions.
> + *
> + * As a future improvement, power consumption could possibly be
> + * decreased in some use cases by enabling and disabling the oscillator
> + * dynamically based on the needs of the child drivers.
> + */
> + ret = regmap_write(adp5585->regmap, regs->gen_cfg,
> + ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG | ADP5585_OSC_EN);
> + if (ret)
> + return ret;
> +
> + return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable, adp5585);
> +}
> +
> +static int adp5585_parse_fw(struct adp5585_dev *adp5585)
> +{
> + unsigned int prop_val;
> + int ret;
> +
> + ret = device_property_read_u32(adp5585->dev, "poll-interval", &prop_val);
> + if (!ret) {
> + adp5585->ev_poll_time = prop_val / 10 - 1;
> + /*
> + * ev_poll_time is the raw value to be written on the register and 0 to 3 are the
> + * valid values.
> + */
> + if (adp5585->ev_poll_time > 3)
> + return dev_err_probe(adp5585->dev, -EINVAL,
> + "Invalid value(%u) for poll-interval\n", prop_val);
> + }
> +
> + return 0;
> +}
> +
> +static void adp5585_irq_disable(void *data)
> +{
> + struct adp5585_dev *adp5585 = data;
> +
> + regmap_write(adp5585->regmap, adp5585->regs->int_en, 0);
> +}
> +
> +static int adp5585_irq_enable(struct i2c_client *i2c,
> + struct adp5585_dev *adp5585)
> +{
> + const struct adp5585_regs *regs = adp5585->regs;
> + unsigned int stat;
> + int ret;
> +
> + if (i2c->irq <= 0)
> + return 0;
> +
> + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq,
> + IRQF_ONESHOT, i2c->name, adp5585);
> + if (ret)
> + return ret;
> +
> + /*
> + * Clear any possible outstanding interrupt before enabling them. We do that by reading
> + * the status register and writing back the same value.
> + */
> + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(adp5585->regmap, regs->int_en, ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN);
> + if (ret)
> + return ret;
> +
> + return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable, adp5585);
> +}
> +
> static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> struct regmap_config regmap_config;
> @@ -271,6 +422,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> "Failed to initialize register map\n");
>
> adp5585->dev = &i2c->dev;
> + adp5585->irq = i2c->irq;
> + BLOCKING_INIT_NOTIFIER_HEAD(&adp5585->event_notifier);
>
> ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
> if (ret)
> @@ -282,29 +435,28 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> - /*
> - * Enable the internal oscillator, as it's shared between multiple
> - * functions.
> - *
> - * As a future improvement, power consumption could possibly be
> - * decreased in some use cases by enabling and disabling the oscillator
> - * dynamically based on the needs of the child drivers.
> - */
> - ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
> + ret = adp5585_parse_fw(adp5585);
> if (ret)
> return ret;
>
> - ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> + ret = adp5585_setup(adp5585);
> if (ret)
> return ret;
>
> - return adp5585_add_devices(adp5585);
> + ret = adp5585_add_devices(adp5585);
> + if (ret)
> + return ret;
> +
> + return adp5585_irq_enable(i2c, adp5585);
> }
>
> static int adp5585_suspend(struct device *dev)
> {
> struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
>
> + if (adp5585->irq)
> + disable_irq(adp5585->irq);
> +
> regcache_cache_only(adp5585->regmap, true);
>
> return 0;
> @@ -313,11 +465,19 @@ static int adp5585_suspend(struct device *dev)
> static int adp5585_resume(struct device *dev)
> {
> struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
> + int ret;
>
> regcache_cache_only(adp5585->regmap, false);
> regcache_mark_dirty(adp5585->regmap);
>
> - return regcache_sync(adp5585->regmap);
> + ret = regcache_sync(adp5585->regmap);
> + if (ret)
> + return ret;
> +
> + if (adp5585->irq)
> + enable_irq(adp5585->irq);
> +
> + return 0;
> }
>
> static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 2a2bccccaa6ca5cba8ff5716c0d3b82d9541432c..b6baf87907a567fe975f8b24f3c36753e6145066 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -10,13 +10,20 @@
> #define __MFD_ADP5585_H_
>
> #include <linux/bits.h>
> +#include <linux/notifier.h>
>
> #define ADP5585_ID 0x00
> #define ADP5585_MAN_ID_VALUE 0x20
> #define ADP5585_MAN_ID_MASK GENMASK(7, 4)
> +#define ADP5585_REV_ID_MASK GENMASK(3, 0)
> #define ADP5585_INT_STATUS 0x01
> +#define ADP5585_OVRFLOW_INT BIT(2)
> +#define ADP5585_EVENT_INT BIT(0)
> #define ADP5585_STATUS 0x02
> +#define ADP5585_EC_MASK GENMASK(4, 0)
> #define ADP5585_FIFO_1 0x03
> +#define ADP5585_KEV_EV_PRESS_MASK BIT(7)
> +#define ADP5585_KEY_EVENT_MASK GENMASK(6, 0)
> #define ADP5585_FIFO_2 0x04
> #define ADP5585_FIFO_3 0x05
> #define ADP5585_FIFO_4 0x06
> @@ -32,6 +39,7 @@
> #define ADP5585_FIFO_14 0x10
> #define ADP5585_FIFO_15 0x11
> #define ADP5585_FIFO_16 0x12
> +#define ADP5585_EV_MAX (ADP5585_FIFO_16 - ADP5585_FIFO_1 + 1)
> #define ADP5585_GPI_INT_STAT_A 0x13
> #define ADP5585_GPI_INT_STAT_B 0x14
> #define ADP5585_GPI_STATUS_A 0x15
> @@ -104,6 +112,8 @@
> #define ADP5585_INT_CFG BIT(1)
> #define ADP5585_RST_CFG BIT(0)
> #define ADP5585_INT_EN 0x3c
> +#define ADP5585_OVRFLOW_IEN BIT(2)
> +#define ADP5585_EVENT_IEN BIT(0)
>
> #define ADP5585_MAX_REG ADP5585_INT_EN
>
> @@ -121,7 +131,9 @@
> #define ADP5589_PWM_OFFT_LOW 0x3e
> #define ADP5589_PWM_ONT_LOW 0x40
> #define ADP5589_PWM_CFG 0x42
> +#define ADP5589_POLL_PTIME_CFG 0x48
> #define ADP5589_PIN_CONFIG_D 0x4C
> +#define ADP5589_GENERAL_CFG 0x4d
> #define ADP5589_INT_EN 0x4e
> #define ADP5589_MAX_REG ADP5589_INT_EN
>
> @@ -142,15 +154,21 @@ enum adp5585_variant {
> };
>
> struct adp5585_regs {
> + unsigned int gen_cfg;
> unsigned int ext_cfg;
> + unsigned int int_en;
> + unsigned int poll_ptime_cfg;
> };
>
> struct adp5585_dev {
> const struct adp5585_regs *regs;
> struct regmap *regmap;
> struct device *dev;
> + struct blocking_notifier_head event_notifier;
> enum adp5585_variant variant;
> unsigned int id;
> + int irq;
> + unsigned int ev_poll_time;
> };
>
> #endif
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (11 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 12/20] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-06-12 14:55 ` Lee Jones
2025-05-21 13:03 ` [PATCH v4 14/20] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
` (6 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The ADP558x family of devices can be programmed to respond to some
especial events, In case of the unlock events, one can lock the keypad
and use KEYS or GPIs events to unlock it. For the reset events, one can
again use a combinations of GPIs/KEYs in order to generate an event that
will trigger the device to generate an output reset pulse.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 270 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/mfd/adp5585.h | 39 +++++++
2 files changed, 308 insertions(+), 1 deletion(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709ca8162dee01 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
.int_en = ADP5585_INT_EN,
.gen_cfg = ADP5585_GENERAL_CFG,
.poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
+ .reset_cfg = ADP5585_RESET_CFG,
+ .reset1_event_a = ADP5585_RESET1_EVENT_A,
+ .reset2_event_a = ADP5585_RESET2_EVENT_A,
};
static const struct adp5585_regs adp5589_regs = {
@@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
.int_en = ADP5589_INT_EN,
.gen_cfg = ADP5589_GENERAL_CFG,
.poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
+ .reset_cfg = ADP5589_RESET_CFG,
+ .reset1_event_a = ADP5589_RESET1_EVENT_A,
+ .reset2_event_a = ADP5589_RESET2_EVENT_A,
};
+static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
+{
+ if (adp5585->has_pin6) {
+ if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <= ADP5585_ROW5_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END)
+ return 0;
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n", ev);
+ }
+
+ if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
+ /* if it's GPI6 */
+ if (ev == (ADP5585_GPI_EVENT_START + 5))
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u). R5 not available\n",
+ ev);
+ return 0;
+ }
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n", ev);
+}
+
+static int adp5589_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
+{
+ if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END)
+ return 0;
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n",
+ ev);
+}
+
static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
struct regmap_config *regmap_config)
{
@@ -191,6 +236,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
*regmap_config = adp5585_regmap_config_template;
adp5585->id = ADP5585_MAN_ID_VALUE;
adp5585->regs = &adp5585_regs;
+ if (adp5585->variant == ADP5585_01)
+ adp5585->has_pin6 = true;
break;
case ADP5589_00:
case ADP5589_01:
@@ -198,6 +245,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
*regmap_config = adp5589_regmap_config_template;
adp5585->id = ADP5589_MAN_ID_VALUE;
adp5585->regs = &adp5589_regs;
+ adp5585->has_unlock = true;
+ adp5585->has_pin6 = true;
break;
default:
return -ENODEV;
@@ -207,6 +256,168 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
return 0;
}
+static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const char *prop, u32 *events,
+ u32 *n_events, u32 max_evs, bool reset_ev)
+{
+ struct device *dev = adp5585->dev;
+ unsigned int ev;
+ int ret;
+
+ /*
+ * The device has the capability of handling special events through GPIs or a Keypad:
+ * unlock events: Unlock the keymap until one of the configured events is detected.
+ * reset events: Generate a reset pulse when one of the configured events is detected.
+ */
+ ret = device_property_count_u32(dev, prop);
+ if (ret < 0)
+ return 0;
+
+ *n_events = ret;
+
+ if (!adp5585->has_unlock && !reset_ev)
+ return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not supported\n");
+
+ if (*n_events > max_evs)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of keys(%u > %u) for %s\n",
+ *n_events, max_evs, prop);
+
+ ret = device_property_read_u32_array(dev, prop, events, *n_events);
+ if (ret)
+ return ret;
+
+ for (ev = 0; ev < *n_events; ev++) {
+ /* for unlock events, 127 is a wildcard */
+ if (!reset_ev && events[ev] == 127)
+ continue;
+
+ if (adp5585->id == ADP5585_MAN_ID_VALUE)
+ ret = adp5585_validate_event(adp5585, events[ev]);
+ else
+ ret = adp5589_validate_event(adp5585, events[ev]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ int ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585->unlock_keys,
+ &adp5585->nkeys_unlock, ARRAY_SIZE(adp5585->unlock_keys),
+ false);
+ if (ret)
+ return ret;
+ if (!adp5585->nkeys_unlock)
+ return 0;
+
+ ret = device_property_read_u32(dev, "adi,unlock-trigger-sec", &adp5585->unlock_time);
+ if (!ret) {
+ if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid unlock time(%u > %d)\n",
+ adp5585->unlock_time,
+ ADP5585_MAX_UNLOCK_TIME_SEC);
+ }
+
+ return 0;
+}
+
+static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ u32 prop_val;
+ int ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585->reset1_keys,
+ &adp5585->nkeys_reset1,
+ ARRAY_SIZE(adp5585->reset1_keys), true);
+ if (ret)
+ return ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events",
+ adp5585->reset2_keys,
+ &adp5585->nkeys_reset2,
+ ARRAY_SIZE(adp5585->reset2_keys), true);
+ if (ret)
+ return ret;
+
+ if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
+ return 0;
+
+ if (adp5585->nkeys_reset1 && device_property_read_bool(dev, "adi,reset1-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
+
+ if (adp5585->nkeys_reset2 && device_property_read_bool(dev, "adi,reset2-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
+
+ if (device_property_read_bool(dev, "adi,rst-passthrough-enable"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN, 1);
+
+ ret = device_property_read_u32(dev, "adi,reset-trigger-ms", &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 0:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0);
+ break;
+ case 1000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1);
+ break;
+ case 1500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2);
+ break;
+ case 2000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3);
+ break;
+ case 2500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4);
+ break;
+ case 3000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5);
+ break;
+ case 3500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6);
+ break;
+ case 4000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7);
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for adi,reset-trigger-ms\n",
+ prop_val);
+ }
+ }
+
+ ret = device_property_read_u32(dev, "adi,reset-pulse-width-us",
+ &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 0);
+ break;
+ case 1000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 1);
+ break;
+ case 2000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 2);
+ break;
+ case 10000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 3);
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for adi,reset-pulse-width-us\n",
+ prop_val);
+ }
+ }
+
+ return 0;
+}
+
static void adp5585_remove_devices(void *dev)
{
mfd_remove_devices(dev);
@@ -307,6 +518,59 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
unsigned int reg_val, i;
int ret;
+ /* Configure the device with reset and unlock events */
+ for (i = 0; i < adp5585->nkeys_unlock; i++) {
+ ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
+ adp5585->unlock_keys[i] | ADP5589_UNLOCK_EV_PRESS);
+ if (ret)
+ return ret;
+ }
+
+ if (adp5585->nkeys_unlock) {
+ ret = regmap_update_bits(adp5585->regmap, ADP5589_UNLOCK_TIMERS,
+ ADP5589_UNLOCK_TIMER, adp5585->unlock_time);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG, ADP5589_LOCK_EN);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < adp5585->nkeys_reset1; i++) {
+ ret = regmap_write(adp5585->regmap, regs->reset1_event_a + i,
+ adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < adp5585->nkeys_reset2; i++) {
+ ret = regmap_write(adp5585->regmap, regs->reset2_event_a + i,
+ adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
+ if (ret)
+ return ret;
+ }
+
+ if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
+ ret = regmap_write(adp5585->regmap, regs->reset_cfg, adp5585->reset_cfg);
+ if (ret)
+ return ret;
+
+ reg_val = 0;
+ /* If there's a reset1 event, then R4 is used as an output for the reset signal */
+ if (adp5585->nkeys_reset1)
+ reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
+ /* If there's a reset2 event, then C4 is used as an output for the reset signal */
+ if (adp5585->nkeys_reset2)
+ reg_val |= ADP5585_C4_EXTEND_CFG_RESET2;
+
+ ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg,
+ ADP5585_C4_EXTEND_CFG_MASK | ADP5585_R4_EXTEND_CFG_MASK,
+ reg_val);
+ if (ret)
+ return ret;
+ }
+
/* Clear any possible event by reading all the FIFO entries */
for (i = 0; i < ADP5585_EV_MAX; i++) {
ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
@@ -351,7 +615,11 @@ static int adp5585_parse_fw(struct adp5585_dev *adp5585)
"Invalid value(%u) for poll-interval\n", prop_val);
}
- return 0;
+ ret = adp5585_unlock_ev_parse(adp5585);
+ if (ret)
+ return ret;
+
+ return adp5585_reset_ev_parse(adp5585);
}
static void adp5585_irq_disable(void *data)
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index b6baf87907a567fe975f8b24f3c36753e6145066..5a1de5ae4bb144ed49a03a4e9e93eb614abe9fa3 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -68,6 +68,7 @@
#define ADP5585_GPIO_DIRECTION_A 0x27
#define ADP5585_GPIO_DIRECTION_B 0x28
#define ADP5585_RESET1_EVENT_A 0x29
+#define ADP5585_RESET_EV_PRESS BIT(7)
#define ADP5585_RESET1_EVENT_B 0x2a
#define ADP5585_RESET1_EVENT_C 0x2b
#define ADP5585_RESET2_EVENT_A 0x2c
@@ -118,6 +119,13 @@
#define ADP5585_MAX_REG ADP5585_INT_EN
#define ADP5585_PIN_MAX 11
+#define ADP5585_MAX_UNLOCK_TIME_SEC 7
+#define ADP5585_KEY_EVENT_START 1
+#define ADP5585_KEY_EVENT_END 25
+#define ADP5585_GPI_EVENT_START 37
+#define ADP5585_GPI_EVENT_END 47
+#define ADP5585_ROW5_KEY_EVENT_START 1
+#define ADP5585_ROW5_KEY_EVENT_END 30
/* ADP5589 */
#define ADP5589_MAN_ID_VALUE 0x10
@@ -128,6 +136,20 @@
#define ADP5589_GPO_DATA_OUT_A 0x2a
#define ADP5589_GPO_OUT_MODE_A 0x2d
#define ADP5589_GPIO_DIRECTION_A 0x30
+#define ADP5589_UNLOCK1 0x33
+#define ADP5589_UNLOCK_EV_PRESS BIT(7)
+#define ADP5589_UNLOCK_TIMERS 0x36
+#define ADP5589_UNLOCK_TIMER GENMASK(2, 0)
+#define ADP5589_LOCK_CFG 0x37
+#define ADP5589_LOCK_EN BIT(0)
+#define ADP5589_RESET1_EVENT_A 0x38
+#define ADP5589_RESET2_EVENT_A 0x3B
+#define ADP5589_RESET_CFG 0x3D
+#define ADP5585_RESET2_POL BIT(7)
+#define ADP5585_RESET1_POL BIT(6)
+#define ADP5585_RST_PASSTHRU_EN BIT(5)
+#define ADP5585_RESET_TRIG_TIME GENMASK(4, 2)
+#define ADP5585_PULSE_WIDTH GENMASK(1, 0)
#define ADP5589_PWM_OFFT_LOW 0x3e
#define ADP5589_PWM_ONT_LOW 0x40
#define ADP5589_PWM_CFG 0x42
@@ -138,6 +160,10 @@
#define ADP5589_MAX_REG ADP5589_INT_EN
#define ADP5589_PIN_MAX 19
+#define ADP5589_KEY_EVENT_START 1
+#define ADP5589_KEY_EVENT_END 88
+#define ADP5589_GPI_EVENT_START 97
+#define ADP5589_GPI_EVENT_END 115
struct regmap;
@@ -158,6 +184,9 @@ struct adp5585_regs {
unsigned int ext_cfg;
unsigned int int_en;
unsigned int poll_ptime_cfg;
+ unsigned int reset_cfg;
+ unsigned int reset1_event_a;
+ unsigned int reset2_event_a;
};
struct adp5585_dev {
@@ -167,8 +196,18 @@ struct adp5585_dev {
struct blocking_notifier_head event_notifier;
enum adp5585_variant variant;
unsigned int id;
+ bool has_unlock;
+ bool has_pin6;
int irq;
unsigned int ev_poll_time;
+ unsigned int unlock_time;
+ unsigned int unlock_keys[2];
+ unsigned int nkeys_unlock;
+ unsigned int reset1_keys[3];
+ unsigned int nkeys_reset1;
+ unsigned int reset2_keys[2];
+ unsigned int nkeys_reset2;
+ u8 reset_cfg;
};
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-05-21 13:03 ` [PATCH v4 13/20] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
@ 2025-06-12 14:55 ` Lee Jones
2025-06-13 9:48 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-06-12 14:55 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP558x family of devices can be programmed to respond to some
> especial events, In case of the unlock events, one can lock the keypad
> and use KEYS or GPIs events to unlock it. For the reset events, one can
> again use a combinations of GPIs/KEYs in order to generate an event that
> will trigger the device to generate an output reset pulse.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 270 +++++++++++++++++++++++++++++++++++++++++++-
> include/linux/mfd/adp5585.h | 39 +++++++
> 2 files changed, 308 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709ca8162dee01 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
> .int_en = ADP5585_INT_EN,
> .gen_cfg = ADP5585_GENERAL_CFG,
> .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> + .reset_cfg = ADP5585_RESET_CFG,
> + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> };
>
> static const struct adp5585_regs adp5589_regs = {
> @@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
> .int_en = ADP5589_INT_EN,
> .gen_cfg = ADP5589_GENERAL_CFG,
> .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> + .reset_cfg = ADP5589_RESET_CFG,
> + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> };
>
> +static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
> +{
> + if (adp5585->has_pin6) {
> + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <= ADP5585_ROW5_KEY_EVENT_END)
> + return 0;
> + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END)
> + return 0;
> +
> + return dev_err_probe(adp5585->dev, -EINVAL,
> + "Invalid unlock/reset event(%u) for this device\n", ev);
> + }
> +
> + if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
> + return 0;
> + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
> + /* if it's GPI6 */
You have to tell us why this is a problem.
Nit: Comments should start with an upper case char.
> + if (ev == (ADP5585_GPI_EVENT_START + 5))
> + return dev_err_probe(adp5585->dev, -EINVAL,
> + "Invalid unlock/reset event(%u). R5 not available\n",
> + ev);
> + return 0;
> + }
> +
> + return dev_err_probe(adp5585->dev, -EINVAL,
> + "Invalid unlock/reset event(%u) for this device\n", ev);
> +}
> +
> +static int adp5589_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
> +{
> + if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END)
> + return 0;
> + if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END)
> + return 0;
> +
> + return dev_err_probe(adp5585->dev, -EINVAL,
> + "Invalid unlock/reset event(%u) for this device\n",
> + ev);
This line break is unnecessary.
> +}
> +
> static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> struct regmap_config *regmap_config)
> {
> @@ -191,6 +236,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> *regmap_config = adp5585_regmap_config_template;
> adp5585->id = ADP5585_MAN_ID_VALUE;
> adp5585->regs = &adp5585_regs;
> + if (adp5585->variant == ADP5585_01)
> + adp5585->has_pin6 = true;
> break;
> case ADP5589_00:
> case ADP5589_01:
> @@ -198,6 +245,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> *regmap_config = adp5589_regmap_config_template;
> adp5585->id = ADP5589_MAN_ID_VALUE;
> adp5585->regs = &adp5589_regs;
> + adp5585->has_unlock = true;
> + adp5585->has_pin6 = true;
> break;
> default:
> return -ENODEV;
> @@ -207,6 +256,168 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> return 0;
> }
>
> +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const char *prop, u32 *events,
> + u32 *n_events, u32 max_evs, bool reset_ev)
> +{
> + struct device *dev = adp5585->dev;
> + unsigned int ev;
> + int ret;
> +
> + /*
> + * The device has the capability of handling special events through GPIs or a Keypad:
> + * unlock events: Unlock the keymap until one of the configured events is detected.
> + * reset events: Generate a reset pulse when one of the configured events is detected.
> + */
> + ret = device_property_count_u32(dev, prop);
> + if (ret < 0)
> + return 0;
> +
> + *n_events = ret;
> +
> + if (!adp5585->has_unlock && !reset_ev)
> + return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not supported\n");
> +
> + if (*n_events > max_evs)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid number of keys(%u > %u) for %s\n",
> + *n_events, max_evs, prop);
> +
> + ret = device_property_read_u32_array(dev, prop, events, *n_events);
> + if (ret)
> + return ret;
> +
> + for (ev = 0; ev < *n_events; ev++) {
> + /* for unlock events, 127 is a wildcard */
As above and throughout the series.
If you define the wildcard magic number you can drop the comment.
> + if (!reset_ev && events[ev] == 127)
> + continue;
> +
> + if (adp5585->id == ADP5585_MAN_ID_VALUE)
> + ret = adp5585_validate_event(adp5585, events[ev]);
> + else
> + ret = adp5589_validate_event(adp5585, events[ev]);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585)
> +{
> + struct device *dev = adp5585->dev;
> + int ret;
> +
> + ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585->unlock_keys,
> + &adp5585->nkeys_unlock, ARRAY_SIZE(adp5585->unlock_keys),
> + false);
> + if (ret)
> + return ret;
> + if (!adp5585->nkeys_unlock)
> + return 0;
> +
> + ret = device_property_read_u32(dev, "adi,unlock-trigger-sec", &adp5585->unlock_time);
> + if (!ret) {
> + if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid unlock time(%u > %d)\n",
> + adp5585->unlock_time,
> + ADP5585_MAX_UNLOCK_TIME_SEC);
> + }
> +
> + return 0;
> +}
> +
> +static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585)
> +{
> + struct device *dev = adp5585->dev;
> + u32 prop_val;
> + int ret;
> +
> + ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585->reset1_keys,
> + &adp5585->nkeys_reset1,
> + ARRAY_SIZE(adp5585->reset1_keys), true);
> + if (ret)
> + return ret;
> +
> + ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events",
> + adp5585->reset2_keys,
> + &adp5585->nkeys_reset2,
> + ARRAY_SIZE(adp5585->reset2_keys), true);
> + if (ret)
> + return ret;
> +
> + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
> + return 0;
> +
> + if (adp5585->nkeys_reset1 && device_property_read_bool(dev, "adi,reset1-active-high"))
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
> +
> + if (adp5585->nkeys_reset2 && device_property_read_bool(dev, "adi,reset2-active-high"))
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
> +
> + if (device_property_read_bool(dev, "adi,rst-passthrough-enable"))
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN, 1);
> +
> + ret = device_property_read_u32(dev, "adi,reset-trigger-ms", &prop_val);
> + if (!ret) {
> + switch (prop_val) {
> + case 0:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0);
> + break;
> + case 1000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1);
> + break;
> + case 1500:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2);
> + break;
> + case 2000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3);
> + break;
> + case 2500:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4);
> + break;
> + case 3000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5);
> + break;
> + case 3500:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6);
> + break;
> + case 4000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7);
> + break;
> + default:
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid value(%u) for adi,reset-trigger-ms\n",
> + prop_val);
> + }
> + }
> +
> + ret = device_property_read_u32(dev, "adi,reset-pulse-width-us",
> + &prop_val);
Odd line break.
> + if (!ret) {
> + switch (prop_val) {
> + case 500:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 0);
> + break;
> + case 1000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 1);
> + break;
> + case 2000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 2);
> + break;
> + case 10000:
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 3);
> + break;
> + default:
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid value(%u) for adi,reset-pulse-width-us\n",
> + prop_val);
> + }
> + }
> +
> + return 0;
> +}
> +
> static void adp5585_remove_devices(void *dev)
> {
> mfd_remove_devices(dev);
> @@ -307,6 +518,59 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> unsigned int reg_val, i;
> int ret;
>
> + /* Configure the device with reset and unlock events */
> + for (i = 0; i < adp5585->nkeys_unlock; i++) {
> + ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
> + adp5585->unlock_keys[i] | ADP5589_UNLOCK_EV_PRESS);
> + if (ret)
> + return ret;
> + }
> +
> + if (adp5585->nkeys_unlock) {
> + ret = regmap_update_bits(adp5585->regmap, ADP5589_UNLOCK_TIMERS,
> + ADP5589_UNLOCK_TIMER, adp5585->unlock_time);
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG, ADP5589_LOCK_EN);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < adp5585->nkeys_reset1; i++) {
> + ret = regmap_write(adp5585->regmap, regs->reset1_event_a + i,
> + adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < adp5585->nkeys_reset2; i++) {
> + ret = regmap_write(adp5585->regmap, regs->reset2_event_a + i,
> + adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
> + if (ret)
> + return ret;
> + }
> +
> + if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
> + ret = regmap_write(adp5585->regmap, regs->reset_cfg, adp5585->reset_cfg);
> + if (ret)
> + return ret;
> +
> + reg_val = 0;
Initialisation during declaration is preferred.
> + /* If there's a reset1 event, then R4 is used as an output for the reset signal */
> + if (adp5585->nkeys_reset1)
> + reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
> + /* If there's a reset2 event, then C4 is used as an output for the reset signal */
> + if (adp5585->nkeys_reset2)
> + reg_val |= ADP5585_C4_EXTEND_CFG_RESET2;
> +
> + ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg,
> + ADP5585_C4_EXTEND_CFG_MASK | ADP5585_R4_EXTEND_CFG_MASK,
> + reg_val);
> + if (ret)
> + return ret;
> + }
> +
> /* Clear any possible event by reading all the FIFO entries */
> for (i = 0; i < ADP5585_EV_MAX; i++) {
> ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
> @@ -351,7 +615,11 @@ static int adp5585_parse_fw(struct adp5585_dev *adp5585)
> "Invalid value(%u) for poll-interval\n", prop_val);
> }
>
> - return 0;
> + ret = adp5585_unlock_ev_parse(adp5585);
> + if (ret)
> + return ret;
> +
> + return adp5585_reset_ev_parse(adp5585);
> }
>
> static void adp5585_irq_disable(void *data)
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index b6baf87907a567fe975f8b24f3c36753e6145066..5a1de5ae4bb144ed49a03a4e9e93eb614abe9fa3 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -68,6 +68,7 @@
> #define ADP5585_GPIO_DIRECTION_A 0x27
> #define ADP5585_GPIO_DIRECTION_B 0x28
> #define ADP5585_RESET1_EVENT_A 0x29
> +#define ADP5585_RESET_EV_PRESS BIT(7)
> #define ADP5585_RESET1_EVENT_B 0x2a
> #define ADP5585_RESET1_EVENT_C 0x2b
> #define ADP5585_RESET2_EVENT_A 0x2c
> @@ -118,6 +119,13 @@
> #define ADP5585_MAX_REG ADP5585_INT_EN
>
> #define ADP5585_PIN_MAX 11
> +#define ADP5585_MAX_UNLOCK_TIME_SEC 7
> +#define ADP5585_KEY_EVENT_START 1
> +#define ADP5585_KEY_EVENT_END 25
> +#define ADP5585_GPI_EVENT_START 37
> +#define ADP5585_GPI_EVENT_END 47
> +#define ADP5585_ROW5_KEY_EVENT_START 1
> +#define ADP5585_ROW5_KEY_EVENT_END 30
>
> /* ADP5589 */
> #define ADP5589_MAN_ID_VALUE 0x10
> @@ -128,6 +136,20 @@
> #define ADP5589_GPO_DATA_OUT_A 0x2a
> #define ADP5589_GPO_OUT_MODE_A 0x2d
> #define ADP5589_GPIO_DIRECTION_A 0x30
> +#define ADP5589_UNLOCK1 0x33
> +#define ADP5589_UNLOCK_EV_PRESS BIT(7)
> +#define ADP5589_UNLOCK_TIMERS 0x36
> +#define ADP5589_UNLOCK_TIMER GENMASK(2, 0)
> +#define ADP5589_LOCK_CFG 0x37
> +#define ADP5589_LOCK_EN BIT(0)
> +#define ADP5589_RESET1_EVENT_A 0x38
> +#define ADP5589_RESET2_EVENT_A 0x3B
> +#define ADP5589_RESET_CFG 0x3D
> +#define ADP5585_RESET2_POL BIT(7)
> +#define ADP5585_RESET1_POL BIT(6)
> +#define ADP5585_RST_PASSTHRU_EN BIT(5)
> +#define ADP5585_RESET_TRIG_TIME GENMASK(4, 2)
> +#define ADP5585_PULSE_WIDTH GENMASK(1, 0)
> #define ADP5589_PWM_OFFT_LOW 0x3e
> #define ADP5589_PWM_ONT_LOW 0x40
> #define ADP5589_PWM_CFG 0x42
> @@ -138,6 +160,10 @@
> #define ADP5589_MAX_REG ADP5589_INT_EN
>
> #define ADP5589_PIN_MAX 19
> +#define ADP5589_KEY_EVENT_START 1
> +#define ADP5589_KEY_EVENT_END 88
> +#define ADP5589_GPI_EVENT_START 97
> +#define ADP5589_GPI_EVENT_END 115
>
> struct regmap;
>
> @@ -158,6 +184,9 @@ struct adp5585_regs {
> unsigned int ext_cfg;
> unsigned int int_en;
> unsigned int poll_ptime_cfg;
> + unsigned int reset_cfg;
> + unsigned int reset1_event_a;
> + unsigned int reset2_event_a;
> };
>
> struct adp5585_dev {
> @@ -167,8 +196,18 @@ struct adp5585_dev {
> struct blocking_notifier_head event_notifier;
> enum adp5585_variant variant;
> unsigned int id;
> + bool has_unlock;
> + bool has_pin6;
> int irq;
> unsigned int ev_poll_time;
> + unsigned int unlock_time;
> + unsigned int unlock_keys[2];
> + unsigned int nkeys_unlock;
> + unsigned int reset1_keys[3];
> + unsigned int nkeys_reset1;
> + unsigned int reset2_keys[2];
> + unsigned int nkeys_reset2;
> + u8 reset_cfg;
> };
>
> #endif
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-06-12 14:55 ` Lee Jones
@ 2025-06-13 9:48 ` Nuno Sá
2025-06-13 13:07 ` Lee Jones
0 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá @ 2025-06-13 9:48 UTC (permalink / raw)
To: Lee Jones, nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Thu, 2025-06-12 at 15:55 +0100, Lee Jones wrote:
> On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > The ADP558x family of devices can be programmed to respond to some
> > especial events, In case of the unlock events, one can lock the keypad
> > and use KEYS or GPIs events to unlock it. For the reset events, one can
> > again use a combinations of GPIs/KEYs in order to generate an event that
> > will trigger the device to generate an output reset pulse.
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 270
> > +++++++++++++++++++++++++++++++++++++++++++-
> > include/linux/mfd/adp5585.h | 39 +++++++
> > 2 files changed, 308 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709ca816
> > 2dee01 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
> > .int_en = ADP5585_INT_EN,
> > .gen_cfg = ADP5585_GENERAL_CFG,
> > .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > + .reset_cfg = ADP5585_RESET_CFG,
> > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > };
> >
> > static const struct adp5585_regs adp5589_regs = {
> > @@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
> > .int_en = ADP5589_INT_EN,
> > .gen_cfg = ADP5589_GENERAL_CFG,
> > .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > + .reset_cfg = ADP5589_RESET_CFG,
> > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > };
> >
> > +static int adp5585_validate_event(const struct adp5585_dev *adp5585,
> > unsigned int ev)
> > +{
> > + if (adp5585->has_pin6) {
> > + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <=
> > ADP5585_ROW5_KEY_EVENT_END)
> > + return 0;
> > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > ADP5585_GPI_EVENT_END)
> > + return 0;
> > +
> > + return dev_err_probe(adp5585->dev, -EINVAL,
> > + "Invalid unlock/reset event(%u) for
> > this device\n", ev);
> > + }
> > +
> > + if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
> > + return 0;
> > + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
> > + /* if it's GPI6 */
>
> You have to tell us why this is a problem.
>
> Nit: Comments should start with an upper case char.
The error message kind of states the problem :). But I'll put it in the comment.
- Nuno Sá
>
> > + if (ev == (ADP5585_GPI_EVENT_START + 5))
> > + return dev_err_probe(adp5585->dev, -EINVAL,
> > + "Invalid unlock/reset
> > event(%u). R5 not available\n",
> > + ev);
> > + return 0;
> > + }
> > +
> > + return dev_err_probe(adp5585->dev, -EINVAL,
> > + "Invalid unlock/reset event(%u) for this
> > device\n", ev);
> > +}
> > +
> > +static int adp5589_validate_event(const struct adp5585_dev *adp5585,
> > unsigned int ev)
> > +{
> > + if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END)
> > + return 0;
> > + if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END)
> > + return 0;
> > +
> > + return dev_err_probe(adp5585->dev, -EINVAL,
> > + "Invalid unlock/reset event(%u) for this
> > device\n",
> > + ev);
>
> This line break is unnecessary.
>
> > +}
> > +
> > static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> > struct regmap_config *regmap_config)
> > {
> > @@ -191,6 +236,8 @@ static int adp5585_fill_variant_config(struct
> > adp5585_dev *adp5585,
> > *regmap_config = adp5585_regmap_config_template;
> > adp5585->id = ADP5585_MAN_ID_VALUE;
> > adp5585->regs = &adp5585_regs;
> > + if (adp5585->variant == ADP5585_01)
> > + adp5585->has_pin6 = true;
> > break;
> > case ADP5589_00:
> > case ADP5589_01:
> > @@ -198,6 +245,8 @@ static int adp5585_fill_variant_config(struct
> > adp5585_dev *adp5585,
> > *regmap_config = adp5589_regmap_config_template;
> > adp5585->id = ADP5589_MAN_ID_VALUE;
> > adp5585->regs = &adp5589_regs;
> > + adp5585->has_unlock = true;
> > + adp5585->has_pin6 = true;
> > break;
> > default:
> > return -ENODEV;
> > @@ -207,6 +256,168 @@ static int adp5585_fill_variant_config(struct
> > adp5585_dev *adp5585,
> > return 0;
> > }
> >
> > +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const
> > char *prop, u32 *events,
> > + u32 *n_events, u32 max_evs, bool
> > reset_ev)
> > +{
> > + struct device *dev = adp5585->dev;
> > + unsigned int ev;
> > + int ret;
> > +
> > + /*
> > + * The device has the capability of handling special events through
> > GPIs or a Keypad:
> > + * unlock events: Unlock the keymap until one of the configured
> > events is detected.
> > + * reset events: Generate a reset pulse when one of the configured
> > events is detected.
> > + */
> > + ret = device_property_count_u32(dev, prop);
> > + if (ret < 0)
> > + return 0;
> > +
> > + *n_events = ret;
> > +
> > + if (!adp5585->has_unlock && !reset_ev)
> > + return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not
> > supported\n");
> > +
> > + if (*n_events > max_evs)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid number of keys(%u > %u) for
> > %s\n",
> > + *n_events, max_evs, prop);
> > +
> > + ret = device_property_read_u32_array(dev, prop, events, *n_events);
> > + if (ret)
> > + return ret;
> > +
> > + for (ev = 0; ev < *n_events; ev++) {
> > + /* for unlock events, 127 is a wildcard */
>
> As above and throughout the series.
>
> If you define the wildcard magic number you can drop the comment.
>
> > + if (!reset_ev && events[ev] == 127)
> > + continue;
> > +
> > + if (adp5585->id == ADP5585_MAN_ID_VALUE)
> > + ret = adp5585_validate_event(adp5585, events[ev]);
> > + else
> > + ret = adp5589_validate_event(adp5585, events[ev]);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585)
> > +{
> > + struct device *dev = adp5585->dev;
> > + int ret;
> > +
> > + ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585-
> > >unlock_keys,
> > + &adp5585->nkeys_unlock,
> > ARRAY_SIZE(adp5585->unlock_keys),
> > + false);
> > + if (ret)
> > + return ret;
> > + if (!adp5585->nkeys_unlock)
> > + return 0;
> > +
> > + ret = device_property_read_u32(dev, "adi,unlock-trigger-sec",
> > &adp5585->unlock_time);
> > + if (!ret) {
> > + if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid unlock time(%u >
> > %d)\n",
> > + adp5585->unlock_time,
> > + ADP5585_MAX_UNLOCK_TIME_SEC);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585)
> > +{
> > + struct device *dev = adp5585->dev;
> > + u32 prop_val;
> > + int ret;
> > +
> > + ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585-
> > >reset1_keys,
> > + &adp5585->nkeys_reset1,
> > + ARRAY_SIZE(adp5585->reset1_keys),
> > true);
> > + if (ret)
> > + return ret;
> > +
> > + ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events",
> > + adp5585->reset2_keys,
> > + &adp5585->nkeys_reset2,
> > + ARRAY_SIZE(adp5585->reset2_keys),
> > true);
> > + if (ret)
> > + return ret;
> > +
> > + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
> > + return 0;
> > +
> > + if (adp5585->nkeys_reset1 && device_property_read_bool(dev,
> > "adi,reset1-active-high"))
> > + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
> > +
> > + if (adp5585->nkeys_reset2 && device_property_read_bool(dev,
> > "adi,reset2-active-high"))
> > + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
> > +
> > + if (device_property_read_bool(dev, "adi,rst-passthrough-enable"))
> > + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN,
> > 1);
> > +
> > + ret = device_property_read_u32(dev, "adi,reset-trigger-ms",
> > &prop_val);
> > + if (!ret) {
> > + switch (prop_val) {
> > + case 0:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0);
> > + break;
> > + case 1000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1);
> > + break;
> > + case 1500:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2);
> > + break;
> > + case 2000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3);
> > + break;
> > + case 2500:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4);
> > + break;
> > + case 3000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5);
> > + break;
> > + case 3500:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6);
> > + break;
> > + case 4000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7);
> > + break;
> > + default:
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid value(%u) for
> > adi,reset-trigger-ms\n",
> > + prop_val);
> > + }
> > + }
> > +
> > + ret = device_property_read_u32(dev, "adi,reset-pulse-width-us",
> > + &prop_val);
>
> Odd line break.
>
> > + if (!ret) {
> > + switch (prop_val) {
> > + case 500:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_PULSE_WIDTH, 0);
> > + break;
> > + case 1000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_PULSE_WIDTH, 1);
> > + break;
> > + case 2000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_PULSE_WIDTH, 2);
> > + break;
> > + case 10000:
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_PULSE_WIDTH, 3);
> > + break;
> > + default:
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid value(%u) for
> > adi,reset-pulse-width-us\n",
> > + prop_val);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static void adp5585_remove_devices(void *dev)
> > {
> > mfd_remove_devices(dev);
> > @@ -307,6 +518,59 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> > unsigned int reg_val, i;
> > int ret;
> >
> > + /* Configure the device with reset and unlock events */
> > + for (i = 0; i < adp5585->nkeys_unlock; i++) {
> > + ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
> > + adp5585->unlock_keys[i] |
> > ADP5589_UNLOCK_EV_PRESS);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + if (adp5585->nkeys_unlock) {
> > + ret = regmap_update_bits(adp5585->regmap,
> > ADP5589_UNLOCK_TIMERS,
> > + ADP5589_UNLOCK_TIMER, adp5585-
> > >unlock_time);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG,
> > ADP5589_LOCK_EN);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < adp5585->nkeys_reset1; i++) {
> > + ret = regmap_write(adp5585->regmap, regs->reset1_event_a +
> > i,
> > + adp5585->reset1_keys[i] |
> > ADP5585_RESET_EV_PRESS);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < adp5585->nkeys_reset2; i++) {
> > + ret = regmap_write(adp5585->regmap, regs->reset2_event_a +
> > i,
> > + adp5585->reset2_keys[i] |
> > ADP5585_RESET_EV_PRESS);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
> > + ret = regmap_write(adp5585->regmap, regs->reset_cfg,
> > adp5585->reset_cfg);
> > + if (ret)
> > + return ret;
> > +
> > + reg_val = 0;
>
> Initialisation during declaration is preferred.
>
> > + /* If there's a reset1 event, then R4 is used as an output
> > for the reset signal */
> > + if (adp5585->nkeys_reset1)
> > + reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
> > + /* If there's a reset2 event, then C4 is used as an output
> > for the reset signal */
> > + if (adp5585->nkeys_reset2)
> > + reg_val |= ADP5585_C4_EXTEND_CFG_RESET2;
> > +
> > + ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg,
> > + ADP5585_C4_EXTEND_CFG_MASK |
> > ADP5585_R4_EXTEND_CFG_MASK,
> > + reg_val);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > /* Clear any possible event by reading all the FIFO entries */
> > for (i = 0; i < ADP5585_EV_MAX; i++) {
> > ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i,
> > ®_val);
> > @@ -351,7 +615,11 @@ static int adp5585_parse_fw(struct adp5585_dev
> > *adp5585)
> > "Invalid value(%u) for poll-
> > interval\n", prop_val);
> > }
> >
> > - return 0;
> > + ret = adp5585_unlock_ev_parse(adp5585);
> > + if (ret)
> > + return ret;
> > +
> > + return adp5585_reset_ev_parse(adp5585);
> > }
> >
> > static void adp5585_irq_disable(void *data)
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index
> > b6baf87907a567fe975f8b24f3c36753e6145066..5a1de5ae4bb144ed49a03a4e9e93eb614a
> > be9fa3 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -68,6 +68,7 @@
> > #define ADP5585_GPIO_DIRECTION_A 0x27
> > #define ADP5585_GPIO_DIRECTION_B 0x28
> > #define ADP5585_RESET1_EVENT_A 0x29
> > +#define ADP5585_RESET_EV_PRESS BIT(7)
> > #define ADP5585_RESET1_EVENT_B 0x2a
> > #define ADP5585_RESET1_EVENT_C 0x2b
> > #define ADP5585_RESET2_EVENT_A 0x2c
> > @@ -118,6 +119,13 @@
> > #define ADP5585_MAX_REG ADP5585_INT_EN
> >
> > #define ADP5585_PIN_MAX 11
> > +#define ADP5585_MAX_UNLOCK_TIME_SEC 7
> > +#define ADP5585_KEY_EVENT_START 1
> > +#define ADP5585_KEY_EVENT_END 25
> > +#define ADP5585_GPI_EVENT_START 37
> > +#define ADP5585_GPI_EVENT_END 47
> > +#define ADP5585_ROW5_KEY_EVENT_START 1
> > +#define ADP5585_ROW5_KEY_EVENT_END 30
> >
> > /* ADP5589 */
> > #define ADP5589_MAN_ID_VALUE 0x10
> > @@ -128,6 +136,20 @@
> > #define ADP5589_GPO_DATA_OUT_A 0x2a
> > #define ADP5589_GPO_OUT_MODE_A 0x2d
> > #define ADP5589_GPIO_DIRECTION_A 0x30
> > +#define ADP5589_UNLOCK1 0x33
> > +#define ADP5589_UNLOCK_EV_PRESS BIT(7)
> > +#define ADP5589_UNLOCK_TIMERS 0x36
> > +#define ADP5589_UNLOCK_TIMER GENMASK(2, 0)
> > +#define ADP5589_LOCK_CFG 0x37
> > +#define ADP5589_LOCK_EN BIT(0)
> > +#define ADP5589_RESET1_EVENT_A 0x38
> > +#define ADP5589_RESET2_EVENT_A 0x3B
> > +#define ADP5589_RESET_CFG 0x3D
> > +#define ADP5585_RESET2_POL BIT(7)
> > +#define ADP5585_RESET1_POL BIT(6)
> > +#define ADP5585_RST_PASSTHRU_EN BIT(5)
> > +#define ADP5585_RESET_TRIG_TIME GENMASK(4,
> > 2)
> > +#define ADP5585_PULSE_WIDTH GENMASK(1, 0)
> > #define ADP5589_PWM_OFFT_LOW 0x3e
> > #define ADP5589_PWM_ONT_LOW 0x40
> > #define ADP5589_PWM_CFG 0x42
> > @@ -138,6 +160,10 @@
> > #define ADP5589_MAX_REG ADP5589_INT_EN
> >
> > #define ADP5589_PIN_MAX 19
> > +#define ADP5589_KEY_EVENT_START 1
> > +#define ADP5589_KEY_EVENT_END 88
> > +#define ADP5589_GPI_EVENT_START 97
> > +#define ADP5589_GPI_EVENT_END 115
> >
> > struct regmap;
> >
> > @@ -158,6 +184,9 @@ struct adp5585_regs {
> > unsigned int ext_cfg;
> > unsigned int int_en;
> > unsigned int poll_ptime_cfg;
> > + unsigned int reset_cfg;
> > + unsigned int reset1_event_a;
> > + unsigned int reset2_event_a;
> > };
> >
> > struct adp5585_dev {
> > @@ -167,8 +196,18 @@ struct adp5585_dev {
> > struct blocking_notifier_head event_notifier;
> > enum adp5585_variant variant;
> > unsigned int id;
> > + bool has_unlock;
> > + bool has_pin6;
> > int irq;
> > unsigned int ev_poll_time;
> > + unsigned int unlock_time;
> > + unsigned int unlock_keys[2];
> > + unsigned int nkeys_unlock;
> > + unsigned int reset1_keys[3];
> > + unsigned int nkeys_reset1;
> > + unsigned int reset2_keys[2];
> > + unsigned int nkeys_reset2;
> > + u8 reset_cfg;
> > };
> >
> > #endif
> >
> > --
> > 2.49.0
> >
> >
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-06-13 9:48 ` Nuno Sá
@ 2025-06-13 13:07 ` Lee Jones
2025-06-13 13:13 ` Nuno Sá
0 siblings, 1 reply; 45+ messages in thread
From: Lee Jones @ 2025-06-13 13:07 UTC (permalink / raw)
To: Nuno Sá
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 13 Jun 2025, Nuno Sá wrote:
> On Thu, 2025-06-12 at 15:55 +0100, Lee Jones wrote:
> > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> >
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > The ADP558x family of devices can be programmed to respond to some
> > > especial events, In case of the unlock events, one can lock the keypad
> > > and use KEYS or GPIs events to unlock it. For the reset events, one can
> > > again use a combinations of GPIs/KEYs in order to generate an event that
> > > will trigger the device to generate an output reset pulse.
> > >
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 270
> > > +++++++++++++++++++++++++++++++++++++++++++-
> > > include/linux/mfd/adp5585.h | 39 +++++++
> > > 2 files changed, 308 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709ca816
> > > 2dee01 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
> > > .int_en = ADP5585_INT_EN,
> > > .gen_cfg = ADP5585_GENERAL_CFG,
> > > .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > > + .reset_cfg = ADP5585_RESET_CFG,
> > > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > > };
> > >
> > > static const struct adp5585_regs adp5589_regs = {
> > > @@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
> > > .int_en = ADP5589_INT_EN,
> > > .gen_cfg = ADP5589_GENERAL_CFG,
> > > .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > > + .reset_cfg = ADP5589_RESET_CFG,
> > > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > > };
> > >
> > > +static int adp5585_validate_event(const struct adp5585_dev *adp5585,
> > > unsigned int ev)
> > > +{
> > > + if (adp5585->has_pin6) {
> > > + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <=
> > > ADP5585_ROW5_KEY_EVENT_END)
> > > + return 0;
> > > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > > ADP5585_GPI_EVENT_END)
> > > + return 0;
> > > +
> > > + return dev_err_probe(adp5585->dev, -EINVAL,
> > > + "Invalid unlock/reset event(%u) for
> > > this device\n", ev);
> > > + }
> > > +
> > > + if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
> > > + return 0;
> > > + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
> > > + /* if it's GPI6 */
> >
> > You have to tell us why this is a problem.
> >
> > Nit: Comments should start with an upper case char.
>
> The error message kind of states the problem :). But I'll put it in the comment.
I don't think it does. Remember, people reading this do not know the
H/W as well as you do. How is GPI6 even related to R5?
> - Nuno Sá
> >
> > > + if (ev == (ADP5585_GPI_EVENT_START + 5))
> > > + return dev_err_probe(adp5585->dev, -EINVAL,
> > > + "Invalid unlock/reset
> > > event(%u). R5 not available\n",
> > > + ev);
> > > + return 0;
> > > + }
[...]
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-06-13 13:07 ` Lee Jones
@ 2025-06-13 13:13 ` Nuno Sá
2025-06-13 13:27 ` Lee Jones
0 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá @ 2025-06-13 13:13 UTC (permalink / raw)
To: Lee Jones
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 2025-06-13 at 14:07 +0100, Lee Jones wrote:
> On Fri, 13 Jun 2025, Nuno Sá wrote:
>
> > On Thu, 2025-06-12 at 15:55 +0100, Lee Jones wrote:
> > > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> > >
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > The ADP558x family of devices can be programmed to respond to some
> > > > especial events, In case of the unlock events, one can lock the keypad
> > > > and use KEYS or GPIs events to unlock it. For the reset events, one can
> > > > again use a combinations of GPIs/KEYs in order to generate an event that
> > > > will trigger the device to generate an output reset pulse.
> > > >
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 270
> > > > +++++++++++++++++++++++++++++++++++++++++++-
> > > > include/linux/mfd/adp5585.h | 39 +++++++
> > > > 2 files changed, 308 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709c
> > > > a816
> > > > 2dee01 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
> > > > .int_en = ADP5585_INT_EN,
> > > > .gen_cfg = ADP5585_GENERAL_CFG,
> > > > .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > > > + .reset_cfg = ADP5585_RESET_CFG,
> > > > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > > > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > > > };
> > > >
> > > > static const struct adp5585_regs adp5589_regs = {
> > > > @@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
> > > > .int_en = ADP5589_INT_EN,
> > > > .gen_cfg = ADP5589_GENERAL_CFG,
> > > > .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > > > + .reset_cfg = ADP5589_RESET_CFG,
> > > > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > > > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > > > };
> > > >
> > > > +static int adp5585_validate_event(const struct adp5585_dev *adp5585,
> > > > unsigned int ev)
> > > > +{
> > > > + if (adp5585->has_pin6) {
> > > > + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <=
> > > > ADP5585_ROW5_KEY_EVENT_END)
> > > > + return 0;
> > > > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > > > ADP5585_GPI_EVENT_END)
> > > > + return 0;
> > > > +
> > > > + return dev_err_probe(adp5585->dev, -EINVAL,
> > > > + "Invalid unlock/reset event(%u)
> > > > for
> > > > this device\n", ev);
> > > > + }
> > > > +
> > > > + if (ev >= ADP5585_KEY_EVENT_START && ev <=
> > > > ADP5585_KEY_EVENT_END)
> > > > + return 0;
> > > > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > > > ADP5585_GPI_EVENT_END) {
> > > > + /* if it's GPI6 */
> > >
> > > You have to tell us why this is a problem.
> > >
> > > Nit: Comments should start with an upper case char.
> >
> > The error message kind of states the problem :). But I'll put it in the
> > comment.
>
> I don't think it does. Remember, people reading this do not know the
> H/W as well as you do. How is GPI6 even related to R5?
Yeah, you might be right. GPI6 is the same pin as R5. In a variation of the chip
we have this extra pin (though the datasheet refers to it as R5) that can either
be used as part of the keypad or a GPIO. In the other variants, it's a reset
pin.
The check is making sure we're not trying to configure an unlock/reset event on
a pin that do not exist. But I get your point, for me it's clear that R5 ==
GPI6. Me not being consistent in the comments/messages won't help anyone reading
the code.
- Nuno Sá
>
> > - Nuno Sá
> > >
> > > > + if (ev == (ADP5585_GPI_EVENT_START + 5))
> > > > + return dev_err_probe(adp5585->dev, -EINVAL,
> > > > + "Invalid unlock/reset
> > > > event(%u). R5 not available\n",
> > > > + ev);
> > > > + return 0;
> > > > + }
>
> [...]
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/20] mfd: adp5585: support reset and unlock events
2025-06-13 13:13 ` Nuno Sá
@ 2025-06-13 13:27 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-13 13:27 UTC (permalink / raw)
To: Nuno Sá
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
On Fri, 13 Jun 2025, Nuno Sá wrote:
> On Fri, 2025-06-13 at 14:07 +0100, Lee Jones wrote:
> > On Fri, 13 Jun 2025, Nuno Sá wrote:
> >
> > > On Thu, 2025-06-12 at 15:55 +0100, Lee Jones wrote:
> > > > On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> > > >
> > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > >
> > > > > The ADP558x family of devices can be programmed to respond to some
> > > > > especial events, In case of the unlock events, one can lock the keypad
> > > > > and use KEYS or GPIs events to unlock it. For the reset events, one can
> > > > > again use a combinations of GPIs/KEYs in order to generate an event that
> > > > > will trigger the device to generate an output reset pulse.
> > > > >
> > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > ---
> > > > > drivers/mfd/adp5585.c | 270
> > > > > +++++++++++++++++++++++++++++++++++++++++++-
> > > > > include/linux/mfd/adp5585.h | 39 +++++++
> > > > > 2 files changed, 308 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > > index
> > > > > dcc09c898dd7990b39e21cb2324fa66ae171a802..6737d622a7ed9f280c439399f3709c
> > > > > a816
> > > > > 2dee01 100644
> > > > > --- a/drivers/mfd/adp5585.c
> > > > > +++ b/drivers/mfd/adp5585.c
> > > > > @@ -170,6 +170,9 @@ static const struct adp5585_regs adp5585_regs = {
> > > > > .int_en = ADP5585_INT_EN,
> > > > > .gen_cfg = ADP5585_GENERAL_CFG,
> > > > > .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > > > > + .reset_cfg = ADP5585_RESET_CFG,
> > > > > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > > > > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > > > > };
> > > > >
> > > > > static const struct adp5585_regs adp5589_regs = {
> > > > > @@ -177,8 +180,50 @@ static const struct adp5585_regs adp5589_regs = {
> > > > > .int_en = ADP5589_INT_EN,
> > > > > .gen_cfg = ADP5589_GENERAL_CFG,
> > > > > .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > > > > + .reset_cfg = ADP5589_RESET_CFG,
> > > > > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > > > > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > > > > };
> > > > >
> > > > > +static int adp5585_validate_event(const struct adp5585_dev *adp5585,
> > > > > unsigned int ev)
> > > > > +{
> > > > > + if (adp5585->has_pin6) {
> > > > > + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <=
> > > > > ADP5585_ROW5_KEY_EVENT_END)
> > > > > + return 0;
> > > > > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > > > > ADP5585_GPI_EVENT_END)
> > > > > + return 0;
> > > > > +
> > > > > + return dev_err_probe(adp5585->dev, -EINVAL,
> > > > > + "Invalid unlock/reset event(%u)
> > > > > for
> > > > > this device\n", ev);
> > > > > + }
> > > > > +
> > > > > + if (ev >= ADP5585_KEY_EVENT_START && ev <=
> > > > > ADP5585_KEY_EVENT_END)
> > > > > + return 0;
> > > > > + if (ev >= ADP5585_GPI_EVENT_START && ev <=
> > > > > ADP5585_GPI_EVENT_END) {
> > > > > + /* if it's GPI6 */
> > > >
> > > > You have to tell us why this is a problem.
> > > >
> > > > Nit: Comments should start with an upper case char.
> > >
> > > The error message kind of states the problem :). But I'll put it in the
> > > comment.
> >
> > I don't think it does. Remember, people reading this do not know the
> > H/W as well as you do. How is GPI6 even related to R5?
>
> Yeah, you might be right. GPI6 is the same pin as R5. In a variation of the chip
> we have this extra pin (though the datasheet refers to it as R5) that can either
> be used as part of the keypad or a GPIO. In the other variants, it's a reset
> pin.
>
> The check is making sure we're not trying to configure an unlock/reset event on
> a pin that do not exist. But I get your point, for me it's clear that R5 ==
> GPI6. Me not being consistent in the comments/messages won't help anyone reading
> the code.
You nailed it! Thanks for understanding.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 14/20] mfd: adp5585: add support for input devices
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (12 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 13/20] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-06-12 15:16 ` Lee Jones
2025-05-21 13:03 ` [PATCH v4 15/20] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
` (5 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The ADP558x family supports a built in keypad matrix decoder which can
be added as an Input device. In order to both support the Input and the
GPIO device, we need to create a bitmap of the supported pins and track
their usage since they can either be used as GPIOs (GPIs) or as part of
the keymap.
We also need to mark special pins busy in case some features are being
used (ex: pwm or reset events).
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 33 +++++++++++++++++++++++++++++++++
include/linux/mfd/adp5585.h | 9 +++++++++
2 files changed, 42 insertions(+)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 6737d622a7ed9f280c439399f3709ca8162dee01..122e2c95385f8d5cbd7839db78dda77ad7ba4ae4 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -22,17 +22,20 @@
enum {
ADP5585_DEV_GPIO,
ADP5585_DEV_PWM,
+ ADP5585_DEV_INPUT,
ADP5585_DEV_MAX
};
static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
MFD_CELL_NAME("adp5585-gpio"),
MFD_CELL_NAME("adp5585-pwm"),
+ MFD_CELL_NAME("adp5585-keys"),
};
static const struct mfd_cell adp5589_devs[] = {
MFD_CELL_NAME("adp5589-gpio"),
MFD_CELL_NAME("adp5589-pwm"),
+ MFD_CELL_NAME("adp5589-keys"),
};
static const struct regmap_range adp5585_volatile_ranges[] = {
@@ -173,6 +176,7 @@ static const struct adp5585_regs adp5585_regs = {
.reset_cfg = ADP5585_RESET_CFG,
.reset1_event_a = ADP5585_RESET1_EVENT_A,
.reset2_event_a = ADP5585_RESET2_EVENT_A,
+ .pin_cfg_a = ADP5585_PIN_CONFIG_A,
};
static const struct adp5585_regs adp5589_regs = {
@@ -183,6 +187,7 @@ static const struct adp5585_regs adp5589_regs = {
.reset_cfg = ADP5589_RESET_CFG,
.reset1_event_a = ADP5589_RESET1_EVENT_A,
.reset2_event_a = ADP5589_RESET2_EVENT_A,
+ .pin_cfg_a = ADP5589_PIN_CONFIG_A,
};
static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
@@ -236,6 +241,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
*regmap_config = adp5585_regmap_config_template;
adp5585->id = ADP5585_MAN_ID_VALUE;
adp5585->regs = &adp5585_regs;
+ adp5585->n_pins = ADP5585_PIN_MAX;
+ adp5585->reset2_out = ADP5585_RESET2_OUT;
if (adp5585->variant == ADP5585_01)
adp5585->has_pin6 = true;
break;
@@ -247,6 +254,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
adp5585->regs = &adp5589_regs;
adp5585->has_unlock = true;
adp5585->has_pin6 = true;
+ adp5585->n_pins = ADP5589_PIN_MAX;
+ adp5585->reset2_out = ADP5589_RESET2_OUT;
break;
default:
return -ENODEV;
@@ -434,6 +443,8 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585)
cells = adp5589_devs;
if (device_property_present(adp5585->dev, "#pwm-cells")) {
+ /* Make sure the PWM output pin is not used by the GPIO or INPUT devices */
+ __set_bit(ADP5585_PWM_OUT, adp5585->pin_usage);
ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
&cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
if (ret)
@@ -449,6 +460,15 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585)
}
}
+ if (device_property_present(adp5585->dev, "adi,keypad-pins")) {
+ ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_INPUT], 1, NULL, 0, NULL);
+ if (ret) {
+ ret = dev_err_probe(adp5585->dev, ret, "Failed to add input device\n");
+ goto out_error;
+ }
+ }
+
return devm_add_action_or_reset(adp5585->dev, adp5585_remove_devices, adp5585->dev);
out_error:
mfd_remove_devices(adp5585->dev);
@@ -518,6 +538,10 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
unsigned int reg_val, i;
int ret;
+ /* if pin_6 (ROW5/GPI6) is not available, make sure to mark it as "busy" */
+ if (!adp5585->has_pin6)
+ __set_bit(5, adp5585->pin_usage);
+
/* Configure the device with reset and unlock events */
for (i = 0; i < adp5585->nkeys_unlock; i++) {
ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
@@ -542,6 +566,9 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
if (ret)
return ret;
+
+ /* mark that pin as not usable for the input and gpio devices */
+ __set_bit(ADP5585_RESET1_OUT, adp5585->pin_usage);
}
for (i = 0; i < adp5585->nkeys_reset2; i++) {
@@ -549,6 +576,8 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
if (ret)
return ret;
+
+ __set_bit(adp5585->reset2_out, adp5585->pin_usage);
}
if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
@@ -703,6 +732,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
+ adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->n_pins, GFP_KERNEL);
+ if (!adp5585->pin_usage)
+ return -ENOMEM;
+
ret = adp5585_parse_fw(adp5585);
if (ret)
return ret;
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 5a1de5ae4bb144ed49a03a4e9e93eb614abe9fa3..5aa042a30c6e9eb2736fb5ab91b505324168d7b5 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -126,6 +126,9 @@
#define ADP5585_GPI_EVENT_END 47
#define ADP5585_ROW5_KEY_EVENT_START 1
#define ADP5585_ROW5_KEY_EVENT_END 30
+#define ADP5585_PWM_OUT 3
+#define ADP5585_RESET1_OUT 4
+#define ADP5585_RESET2_OUT 9
/* ADP5589 */
#define ADP5589_MAN_ID_VALUE 0x10
@@ -154,6 +157,7 @@
#define ADP5589_PWM_ONT_LOW 0x40
#define ADP5589_PWM_CFG 0x42
#define ADP5589_POLL_PTIME_CFG 0x48
+#define ADP5589_PIN_CONFIG_A 0x49
#define ADP5589_PIN_CONFIG_D 0x4C
#define ADP5589_GENERAL_CFG 0x4d
#define ADP5589_INT_EN 0x4e
@@ -164,6 +168,7 @@
#define ADP5589_KEY_EVENT_END 88
#define ADP5589_GPI_EVENT_START 97
#define ADP5589_GPI_EVENT_END 115
+#define ADP5589_RESET2_OUT 12
struct regmap;
@@ -187,6 +192,7 @@ struct adp5585_regs {
unsigned int reset_cfg;
unsigned int reset1_event_a;
unsigned int reset2_event_a;
+ unsigned int pin_cfg_a;
};
struct adp5585_dev {
@@ -194,6 +200,9 @@ struct adp5585_dev {
struct regmap *regmap;
struct device *dev;
struct blocking_notifier_head event_notifier;
+ unsigned long *pin_usage;
+ unsigned int n_pins;
+ unsigned int reset2_out;
enum adp5585_variant variant;
unsigned int id;
bool has_unlock;
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 14/20] mfd: adp5585: add support for input devices
2025-05-21 13:03 ` [PATCH v4 14/20] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
@ 2025-06-12 15:16 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 15:16 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP558x family supports a built in keypad matrix decoder which can
> be added as an Input device. In order to both support the Input and the
> GPIO device, we need to create a bitmap of the supported pins and track
> their usage since they can either be used as GPIOs (GPIs) or as part of
> the keymap.
>
> We also need to mark special pins busy in case some features are being
> used (ex: pwm or reset events).
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 33 +++++++++++++++++++++++++++++++++
> include/linux/mfd/adp5585.h | 9 +++++++++
> 2 files changed, 42 insertions(+)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 6737d622a7ed9f280c439399f3709ca8162dee01..122e2c95385f8d5cbd7839db78dda77ad7ba4ae4 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -22,17 +22,20 @@
> enum {
> ADP5585_DEV_GPIO,
> ADP5585_DEV_PWM,
> + ADP5585_DEV_INPUT,
> ADP5585_DEV_MAX
> };
>
> static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
> MFD_CELL_NAME("adp5585-gpio"),
> MFD_CELL_NAME("adp5585-pwm"),
> + MFD_CELL_NAME("adp5585-keys"),
> };
>
> static const struct mfd_cell adp5589_devs[] = {
> MFD_CELL_NAME("adp5589-gpio"),
> MFD_CELL_NAME("adp5589-pwm"),
> + MFD_CELL_NAME("adp5589-keys"),
> };
>
> static const struct regmap_range adp5585_volatile_ranges[] = {
> @@ -173,6 +176,7 @@ static const struct adp5585_regs adp5585_regs = {
> .reset_cfg = ADP5585_RESET_CFG,
> .reset1_event_a = ADP5585_RESET1_EVENT_A,
> .reset2_event_a = ADP5585_RESET2_EVENT_A,
> + .pin_cfg_a = ADP5585_PIN_CONFIG_A,
> };
>
> static const struct adp5585_regs adp5589_regs = {
> @@ -183,6 +187,7 @@ static const struct adp5585_regs adp5589_regs = {
> .reset_cfg = ADP5589_RESET_CFG,
> .reset1_event_a = ADP5589_RESET1_EVENT_A,
> .reset2_event_a = ADP5589_RESET2_EVENT_A,
> + .pin_cfg_a = ADP5589_PIN_CONFIG_A,
> };
>
> static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
> @@ -236,6 +241,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> *regmap_config = adp5585_regmap_config_template;
> adp5585->id = ADP5585_MAN_ID_VALUE;
> adp5585->regs = &adp5585_regs;
> + adp5585->n_pins = ADP5585_PIN_MAX;
> + adp5585->reset2_out = ADP5585_RESET2_OUT;
> if (adp5585->variant == ADP5585_01)
> adp5585->has_pin6 = true;
> break;
> @@ -247,6 +254,8 @@ static int adp5585_fill_variant_config(struct adp5585_dev *adp5585,
> adp5585->regs = &adp5589_regs;
> adp5585->has_unlock = true;
> adp5585->has_pin6 = true;
> + adp5585->n_pins = ADP5589_PIN_MAX;
> + adp5585->reset2_out = ADP5589_RESET2_OUT;
> break;
> default:
> return -ENODEV;
> @@ -434,6 +443,8 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585)
> cells = adp5589_devs;
>
> if (device_property_present(adp5585->dev, "#pwm-cells")) {
> + /* Make sure the PWM output pin is not used by the GPIO or INPUT devices */
Nit: Remove the double space.
> + __set_bit(ADP5585_PWM_OUT, adp5585->pin_usage);
> ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
> &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
> if (ret)
> @@ -449,6 +460,15 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585)
> }
> }
>
> + if (device_property_present(adp5585->dev, "adi,keypad-pins")) {
> + ret = mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
> + &cells[ADP5585_DEV_INPUT], 1, NULL, 0, NULL);
> + if (ret) {
> + ret = dev_err_probe(adp5585->dev, ret, "Failed to add input device\n");
> + goto out_error;
> + }
> + }
> +
> return devm_add_action_or_reset(adp5585->dev, adp5585_remove_devices, adp5585->dev);
> out_error:
> mfd_remove_devices(adp5585->dev);
> @@ -518,6 +538,10 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> unsigned int reg_val, i;
> int ret;
>
> + /* if pin_6 (ROW5/GPI6) is not available, make sure to mark it as "busy" */
Same thing about comments (I'll not mention it again).
> + if (!adp5585->has_pin6)
> + __set_bit(5, adp5585->pin_usage);
Please define all magic numbers.
> +
> /* Configure the device with reset and unlock events */
> for (i = 0; i < adp5585->nkeys_unlock; i++) {
> ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
> @@ -542,6 +566,9 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
> if (ret)
> return ret;
> +
> + /* mark that pin as not usable for the input and gpio devices */
Input or INPUT and GPIO.
> + __set_bit(ADP5585_RESET1_OUT, adp5585->pin_usage);
> }
>
> for (i = 0; i < adp5585->nkeys_reset2; i++) {
> @@ -549,6 +576,8 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
> if (ret)
> return ret;
> +
> + __set_bit(adp5585->reset2_out, adp5585->pin_usage);
> }
>
> if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
> @@ -703,6 +732,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> + adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->n_pins, GFP_KERNEL);
> + if (!adp5585->pin_usage)
> + return -ENOMEM;
> +
> ret = adp5585_parse_fw(adp5585);
> if (ret)
> return ret;
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 5a1de5ae4bb144ed49a03a4e9e93eb614abe9fa3..5aa042a30c6e9eb2736fb5ab91b505324168d7b5 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -126,6 +126,9 @@
> #define ADP5585_GPI_EVENT_END 47
> #define ADP5585_ROW5_KEY_EVENT_START 1
> #define ADP5585_ROW5_KEY_EVENT_END 30
> +#define ADP5585_PWM_OUT 3
> +#define ADP5585_RESET1_OUT 4
> +#define ADP5585_RESET2_OUT 9
>
> /* ADP5589 */
> #define ADP5589_MAN_ID_VALUE 0x10
> @@ -154,6 +157,7 @@
> #define ADP5589_PWM_ONT_LOW 0x40
> #define ADP5589_PWM_CFG 0x42
> #define ADP5589_POLL_PTIME_CFG 0x48
> +#define ADP5589_PIN_CONFIG_A 0x49
> #define ADP5589_PIN_CONFIG_D 0x4C
> #define ADP5589_GENERAL_CFG 0x4d
> #define ADP5589_INT_EN 0x4e
> @@ -164,6 +168,7 @@
> #define ADP5589_KEY_EVENT_END 88
> #define ADP5589_GPI_EVENT_START 97
> #define ADP5589_GPI_EVENT_END 115
> +#define ADP5589_RESET2_OUT 12
>
> struct regmap;
>
> @@ -187,6 +192,7 @@ struct adp5585_regs {
> unsigned int reset_cfg;
> unsigned int reset1_event_a;
> unsigned int reset2_event_a;
> + unsigned int pin_cfg_a;
> };
>
> struct adp5585_dev {
> @@ -194,6 +200,9 @@ struct adp5585_dev {
> struct regmap *regmap;
> struct device *dev;
> struct blocking_notifier_head event_notifier;
> + unsigned long *pin_usage;
> + unsigned int n_pins;
> + unsigned int reset2_out;
> enum adp5585_variant variant;
> unsigned int id;
> bool has_unlock;
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 15/20] gpio: adp5585: support gpi events
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (13 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 14/20] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 16/20] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
` (4 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying, Bartosz Golaszewski
From: Nuno Sá <nuno.sa@analog.com>
Add support for adding GPIs to the event FIFO. This is done by adding
irq_chip support. Like this, one can use the input gpio_keys driver as a
"frontend" device and input handler.
As part of this change, we now implement .request() and .free() as we can't
blindly consume all available pins as GPIOs (example: some pins can be
used for forming a keymap matrix).
Also note that the number of pins can now be obtained from the parent,
top level device. Hence the 'max_gpio' variable can be removed.
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-adp5585.c | 221 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/mfd/adp5585.h | 2 +
3 files changed, 220 insertions(+), 4 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9ae806f45e19c1494d156b7f04b1882be68d3e3f..0b85d07ccb0b8a41f33fd3d930eb74f70787355d 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1259,6 +1259,7 @@ config GPIO_ADP5520
config GPIO_ADP5585
tristate "GPIO Support for ADP5585"
depends on MFD_ADP5585
+ select GPIOLIB_IRQCHIP
help
This option enables support for the GPIO function found in the Analog
Devices ADP5585.
diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
index cdf107742579cb44d73cc030646358ba5a23fd97..b2c8836c5f8477ebeea516b4eedc7d3d2aad59dd 100644
--- a/drivers/gpio/gpio-adp5585.c
+++ b/drivers/gpio/gpio-adp5585.c
@@ -7,10 +7,15 @@
* Copyright 2025 Analog Devices, Inc.
*/
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/adp5585.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
@@ -36,20 +41,29 @@
struct adp5585_gpio_chip {
int (*bank)(unsigned int off);
int (*bit)(unsigned int off);
- unsigned int max_gpio;
unsigned int debounce_dis_a;
unsigned int rpull_cfg_a;
unsigned int gpo_data_a;
unsigned int gpo_out_a;
unsigned int gpio_dir_a;
unsigned int gpi_stat_a;
+ unsigned int gpi_int_lvl_a;
+ unsigned int gpi_ev_a;
+ unsigned int gpi_ev_min;
+ unsigned int gpi_ev_max;
bool has_bias_hole;
};
struct adp5585_gpio_dev {
struct gpio_chip gpio_chip;
+ struct notifier_block nb;
const struct adp5585_gpio_chip *info;
struct regmap *regmap;
+ unsigned long irq_mask;
+ unsigned long irq_en;
+ unsigned long irq_active_high;
+ /* used for irqchip bus locking */
+ struct mutex bus_lock;
};
static int adp5585_gpio_bank(unsigned int off)
@@ -224,12 +238,175 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
};
}
+static int adp5585_gpio_request(struct gpio_chip *chip, unsigned int off)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ struct device *dev = chip->parent;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+ const struct adp5585_regs *regs = adp5585->regs;
+ int ret;
+
+ ret = test_and_set_bit(off, adp5585->pin_usage);
+ if (ret)
+ return -EBUSY;
+
+ /* make sure it's configured for GPIO */
+ return regmap_clear_bits(adp5585_gpio->regmap,
+ regs->pin_cfg_a + info->bank(off),
+ info->bit(off));
+}
+
+static void adp5585_gpio_free(struct gpio_chip *chip, unsigned int off)
+{
+ struct device *dev = chip->parent;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+
+ clear_bit(off, adp5585->pin_usage);
+}
+
+static int adp5585_gpio_key_event(struct notifier_block *nb, unsigned long key,
+ void *data)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = container_of(nb, struct adp5585_gpio_dev, nb);
+ struct device *dev = adp5585_gpio->gpio_chip.parent;
+ unsigned long key_press = (unsigned long)data;
+ unsigned int irq, irq_type;
+ struct irq_data *irqd;
+ bool active_high;
+ unsigned int off;
+
+ /* make sure the event is for me */
+ if (key < adp5585_gpio->info->gpi_ev_min || key > adp5585_gpio->info->gpi_ev_max)
+ return NOTIFY_DONE;
+
+ off = key - adp5585_gpio->info->gpi_ev_min;
+ active_high = test_bit(off, &adp5585_gpio->irq_active_high);
+
+ irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off);
+ if (!irq)
+ return NOTIFY_BAD;
+
+ irqd = irq_get_irq_data(irq);
+ if (!irqd) {
+ dev_err(dev, "Could not get irq(%u) data\n", irq);
+ return NOTIFY_BAD;
+ }
+
+ dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%lu, a_high=%u\n",
+ off, key_press, active_high);
+
+ if (!active_high)
+ key_press = !key_press;
+
+ irq_type = irqd_get_trigger_type(irqd);
+
+ if ((irq_type & IRQ_TYPE_EDGE_RISING && key_press) ||
+ (irq_type & IRQ_TYPE_EDGE_FALLING && !key_press))
+ handle_nested_irq(irq);
+
+ return NOTIFY_STOP;
+}
+
+static void adp5585_irq_bus_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+
+ mutex_lock(&adp5585_gpio->bus_lock);
+}
+
+static void adp5585_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ const struct adp5585_gpio_chip *info = adp5585_gpio->info;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ bool active_high = test_bit(hwirq, &adp5585_gpio->irq_active_high);
+ bool enabled = test_bit(hwirq, &adp5585_gpio->irq_en);
+ bool masked = test_bit(hwirq, &adp5585_gpio->irq_mask);
+ unsigned int bank = adp5585_gpio->info->bank(hwirq);
+ unsigned int bit = adp5585_gpio->info->bit(hwirq);
+
+ if (masked && !enabled)
+ goto out_unlock;
+ if (!masked && enabled)
+ goto out_unlock;
+
+ regmap_update_bits(adp5585_gpio->regmap, info->gpi_int_lvl_a + bank, bit,
+ active_high ? bit : 0);
+ regmap_update_bits(adp5585_gpio->regmap, info->gpi_ev_a + bank, bit,
+ masked ? 0 : bit);
+ assign_bit(hwirq, &adp5585_gpio->irq_en, !masked);
+
+out_unlock:
+ mutex_unlock(&adp5585_gpio->bus_lock);
+}
+
+static void adp5585_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ __set_bit(hwirq, &adp5585_gpio->irq_mask);
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static void adp5585_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+ __clear_bit(hwirq, &adp5585_gpio->irq_mask);
+}
+
+static int adp5585_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ if (!(type & IRQ_TYPE_EDGE_BOTH))
+ return -EINVAL;
+
+ assign_bit(hwirq, &adp5585_gpio->irq_active_high,
+ type == IRQ_TYPE_EDGE_RISING);
+
+ irq_set_handler_locked(d, handle_edge_irq);
+ return 0;
+}
+
+static const struct irq_chip adp5585_irq_chip = {
+ .name = "adp5585",
+ .irq_mask = adp5585_irq_mask,
+ .irq_unmask = adp5585_irq_unmask,
+ .irq_bus_lock = adp5585_irq_bus_lock,
+ .irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock,
+ .irq_set_type = adp5585_irq_set_type,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static void adp5585_gpio_unreg_notifier(void *data)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = data;
+ struct device *dev = adp5585_gpio->gpio_chip.parent;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+
+ blocking_notifier_chain_unregister(&adp5585->event_notifier,
+ &adp5585_gpio->nb);
+}
+
static int adp5585_gpio_probe(struct platform_device *pdev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
const struct platform_device_id *id = platform_get_device_id(pdev);
struct adp5585_gpio_dev *adp5585_gpio;
struct device *dev = &pdev->dev;
+ struct gpio_irq_chip *girq;
struct gpio_chip *gc;
int ret;
@@ -253,13 +430,43 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
gc->get = adp5585_gpio_get_value;
gc->set_rv = adp5585_gpio_set_value;
gc->set_config = adp5585_gpio_set_config;
+ gc->request = adp5585_gpio_request;
+ gc->free = adp5585_gpio_free;
gc->can_sleep = true;
gc->base = -1;
- gc->ngpio = adp5585_gpio->info->max_gpio;
+ gc->ngpio = adp5585->n_pins;
gc->label = pdev->name;
gc->owner = THIS_MODULE;
+ if (device_property_present(dev->parent, "interrupt-controller")) {
+ if (!adp5585->irq)
+ return dev_err_probe(dev, -EINVAL,
+ "Unable to serve as interrupt controller without IRQ\n");
+
+ girq = &adp5585_gpio->gpio_chip.irq;
+ gpio_irq_chip_set_chip(girq, &adp5585_irq_chip);
+ girq->handler = handle_bad_irq;
+ girq->threaded = true;
+
+ adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event;
+ ret = blocking_notifier_chain_register(&adp5585->event_notifier,
+ &adp5585_gpio->nb);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier,
+ adp5585_gpio);
+ if (ret)
+ return ret;
+ }
+
+ /* everything masked by default */
+ adp5585_gpio->irq_mask = ~0UL;
+
+ ret = devm_mutex_init(dev, &adp5585_gpio->bus_lock);
+ if (ret)
+ return ret;
ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip,
adp5585_gpio);
if (ret)
@@ -277,8 +484,11 @@ static const struct adp5585_gpio_chip adp5585_gpio_chip_info = {
.gpo_out_a = ADP5585_GPO_OUT_MODE_A,
.gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
.gpi_stat_a = ADP5585_GPI_STATUS_A,
- .max_gpio = ADP5585_PIN_MAX,
.has_bias_hole = true,
+ .gpi_ev_min = ADP5585_GPI_EVENT_START,
+ .gpi_ev_max = ADP5585_GPI_EVENT_END,
+ .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
+ .gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
};
static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
@@ -290,7 +500,10 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
.gpo_out_a = ADP5589_GPO_OUT_MODE_A,
.gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
.gpi_stat_a = ADP5589_GPI_STATUS_A,
- .max_gpio = ADP5589_PIN_MAX,
+ .gpi_ev_min = ADP5589_GPI_EVENT_START,
+ .gpi_ev_max = ADP5589_GPI_EVENT_END,
+ .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
+ .gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
};
static const struct platform_device_id adp5585_gpio_id_table[] = {
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 5aa042a30c6e9eb2736fb5ab91b505324168d7b5..bb103eeb26537a20b47b926a24445c601d55f0ec 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -135,6 +135,8 @@
#define ADP5589_GPI_STATUS_A 0x16
#define ADP5589_GPI_STATUS_C 0x18
#define ADP5589_RPULL_CONFIG_A 0x19
+#define ADP5589_GPI_INT_LEVEL_A 0x1e
+#define ADP5589_GPI_EVENT_EN_A 0x21
#define ADP5589_DEBOUNCE_DIS_A 0x27
#define ADP5589_GPO_DATA_OUT_A 0x2a
#define ADP5589_GPO_OUT_MODE_A 0x2d
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 16/20] Input: adp5585: Add Analog Devices ADP5585/89 support
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (14 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 15/20] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 17/20] Input: adp5589: remove the driver Nuno Sá via B4 Relay
` (3 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The ADP5585 is a 10/11 input/output port expander with a built in keypad
matrix decoder, programmable logic, reset generator, and PWM generator.
This driver supports the keyboard function using the platform device
registered by the core MFD driver.
The ADP5589 has 19 pins and also features an unlock function.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
MAINTAINERS | 1 +
drivers/input/keyboard/Kconfig | 11 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/adp5585-keys.c | 371 ++++++++++++++++++++++++++++++++++
4 files changed, 384 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0737dcb2e41119426f1d8fbaec829cc90ed0bf64..18838ba19e5edbbe352a470c4e177c6d24136d83 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -550,6 +550,7 @@ L: linux-pwm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml
F: drivers/gpio/gpio-adp5585.c
+F: drivers/input/keyboard/adp5585-keys.c
F: drivers/mfd/adp5585.c
F: drivers/pwm/pwm-adp5585.c
F: include/linux/mfd/adp5585.h
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 721ab69e84ac6586f4f19102890a15ca3fcf1910..322da0957067db77c7f66ab26a181d39c2c1d513 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -37,6 +37,17 @@ config KEYBOARD_ADP5520
To compile this driver as a module, choose M here: the module will
be called adp5520-keys.
+config KEYBOARD_ADP5585
+ tristate "ADP5585 and similar I2C QWERTY Keypad and IO Expanders"
+ depends on MFD_ADP5585
+ select INPUT_MATRIXKMAP
+ help
+ This option enables support for the KEYMAP function found in the Analog
+ Devices ADP5585 and similar devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adp5585-keys.
+
config KEYBOARD_ADP5588
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
depends on I2C
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1e0721c3070968a6339a42f65a95af48364f6897..f00ec003a59aa28577ae164c0539cc5aff9579fc 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
+obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
diff --git a/drivers/input/keyboard/adp5585-keys.c b/drivers/input/keyboard/adp5585-keys.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7ace33a320dd7685deb4e06bf593df16f3f6337
--- /dev/null
+++ b/drivers/input/keyboard/adp5585-keys.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADP5585 Keys driver
+ *
+ * Copyright (C) 2025 Analog Devices, Inc.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/find.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/adp5585.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+/* As needed for the matrix parsing code */
+#define ADP5589_MAX_KEYMAPSIZE 123
+
+struct adp5585_kpad_chip {
+ u8 key_ev_min;
+ u8 key_ev_max;
+ u8 max_rows;
+ u8 max_cols;
+};
+
+struct adp5585_kpad {
+ const struct adp5585_kpad_chip *info;
+ struct notifier_block nb;
+ struct input_dev *input;
+ unsigned short keycode[ADP5589_MAX_KEYMAPSIZE];
+ struct device *dev;
+ unsigned long keypad;
+ int row_shift;
+};
+
+static int adp5585_keys_validate_events(const struct adp5585_kpad *kpad,
+ const u32 *events, u32 n_events)
+{
+ unsigned int ev;
+ u32 row, col;
+
+ for (ev = 0; ev < n_events; ev++) {
+ if (events[ev] < kpad->info->key_ev_min ||
+ events[ev] > kpad->info->key_ev_max)
+ continue;
+
+ /*
+ * if the event is to be generated by the keymap, we need to make
+ * sure that the pins are part of it!
+ */
+ row = (events[ev] - 1) / kpad->info->max_cols;
+ col = (events[ev] - 1) % kpad->info->max_cols;
+
+ if (test_bit(row, &kpad->keypad) &&
+ test_bit(col + kpad->info->max_rows, &kpad->keypad))
+ continue;
+
+ return dev_err_probe(kpad->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) not used in the keypad\n",
+ events[ev]);
+ }
+
+ return 0;
+}
+
+static int adp5585_keys_check_special_events(const struct adp5585_dev *adp5585,
+ const struct adp5585_kpad *kpad)
+{
+ int error;
+
+ error = adp5585_keys_validate_events(kpad, adp5585->unlock_keys,
+ adp5585->nkeys_unlock);
+ if (error)
+ return error;
+
+ error = adp5585_keys_validate_events(kpad, adp5585->reset1_keys,
+ adp5585->nkeys_reset1);
+ if (error)
+ return error;
+
+ return adp5585_keys_validate_events(kpad, adp5585->reset2_keys,
+ adp5585->nkeys_reset2);
+}
+
+static void adp5585_keys_pins_free(void *data)
+{
+ struct adp5585_kpad *kpad = data;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent);
+ unsigned int pin;
+
+ for_each_set_bit(pin, &kpad->keypad, adp5585->n_pins)
+ clear_bit(pin, adp5585->pin_usage);
+}
+
+static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
+ struct adp5585_kpad *kpad)
+{
+ struct device *dev = kpad->dev;
+ u32 cols = 0, rows = 0, pin;
+ int error, n_pins;
+
+ /*
+ * We do not check for errors (or no value) since the input device is
+ * only added if this property is present in the first place.
+ */
+ n_pins = device_property_count_u32(dev, "adi,keypad-pins");
+ if (n_pins > adp5585->n_pins)
+ return dev_err_probe(dev, -EINVAL,
+ "Too many keypad pins (%d) defined (max=%d)\n",
+ n_pins, adp5585->n_pins);
+
+ unsigned int *keypad_pins __free(kfree) = kcalloc(n_pins, sizeof(*keypad_pins),
+ GFP_KERNEL);
+ if (!keypad_pins)
+ return -ENOMEM;
+
+ error = device_property_read_u32_array(dev, "adi,keypad-pins",
+ keypad_pins, n_pins);
+ if (error)
+ return error;
+
+ /*
+ * We can add the action here since it makes the code easier and nothing
+ * "bad" will happen out of it. Worst case, it will be a no-op and no
+ * bit will set.
+ */
+ error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad);
+ if (error)
+ return error;
+
+ for (pin = 0; pin < n_pins; pin++) {
+ if (keypad_pins[pin] >= adp5585->n_pins)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid keypad pin(%u) defined\n",
+ keypad_pins[pin]);
+
+ if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage))
+ return dev_err_probe(dev, -EBUSY,
+ "Keypad pin(%u) already used\n",
+ keypad_pins[pin]);
+
+ __set_bit(keypad_pins[pin], &kpad->keypad);
+ }
+
+ /*
+ * Note that given that we get a mask (and the HW allows it), we
+ * can have holes in our keypad (eg: row0, row1 and row7 enabled).
+ * However, for the matrix parsing functions we need to pass the
+ * number of rows/cols as the maximum row/col used plus 1. This
+ * pretty much means we will also have holes in our SW keypad.
+ */
+
+ rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1;
+ if (rows == kpad->info->max_rows + 1)
+ return dev_err_probe(dev, -EINVAL,
+ "Now rows defined in the keypad!\n");
+
+ cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad->info->max_rows);
+ if (cols < kpad->info->max_rows)
+ return dev_err_probe(dev, -EINVAL,
+ "No columns defined in the keypad!\n");
+
+ cols = cols + 1 - kpad->info->max_rows;
+
+ error = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
+ kpad->keycode, kpad->input);
+ if (error)
+ return error;
+
+ kpad->row_shift = get_count_order(cols);
+
+ if (device_property_read_bool(kpad->dev, "autorepeat"))
+ __set_bit(EV_REP, kpad->input->evbit);
+
+ error = adp5585_keys_check_special_events(adp5585, kpad);
+ if (error)
+ return error;
+
+ return 0
+}
+
+static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
+ struct adp5585_kpad *kpad)
+{
+ unsigned long keys_bits, start = 0, nbits = kpad->info->max_rows;
+ const struct adp5585_regs *regs = adp5585->regs;
+ unsigned int i = 0, max_cols = kpad->info->max_cols;
+ int error;
+
+ /*
+ * Take care as the below assumes max_rows is always less or equal than
+ * 8 which is true for the supported devices. If we happen to add
+ * another device we need to make sure this still holds true. Although
+ * adding a new device is very unlikely.
+ */
+ do {
+ keys_bits = bitmap_read(&kpad->keypad, start, nbits);
+ if (keys_bits) {
+ error = regmap_write(adp5585->regmap, regs->pin_cfg_a + i,
+ keys_bits);
+ if (error)
+ return error;
+ }
+
+ start += nbits;
+ if (max_cols > 8) {
+ nbits = 8;
+ max_cols -= nbits;
+ } else {
+ nbits = max_cols;
+ }
+
+ i++;
+ } while (start < kpad->info->max_rows + kpad->info->max_cols);
+
+ return 0;
+}
+
+static int adp5585_keys_ev_handle(struct notifier_block *nb, unsigned long key,
+ void *data)
+{
+ struct adp5585_kpad *kpad = container_of(nb, struct adp5585_kpad, nb);
+ unsigned long key_press = (unsigned long)data;
+ unsigned int row, col, code;
+
+ /* make sure the event is for us */
+ if (key < kpad->info->key_ev_min || key > kpad->info->key_ev_max)
+ return NOTIFY_DONE;
+
+ /*
+ * Unlikely but lets be on the safe side! We do not return any error
+ * because the event was indeed for us but with some weird value. So,
+ * we still want the caller know that the right handler was called.
+ */
+ if (!key)
+ return NOTIFY_BAD;
+
+ row = (key - 1) / (kpad->info->max_cols);
+ col = (key - 1) % (kpad->info->max_cols);
+ code = MATRIX_SCAN_CODE(row, col, kpad->row_shift);
+
+ dev_dbg_ratelimited(kpad->dev, "report key(%lu) r(%d) c(%d) code(%d)\n",
+ key, row, col, kpad->keycode[code]);
+
+ input_report_key(kpad->input, kpad->keycode[code], key_press);
+ input_sync(kpad->input);
+
+ return NOTIFY_STOP;
+}
+
+static void adp5585_keys_unreg_notifier(void *data)
+{
+ struct adp5585_kpad *kpad = data;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent);
+
+ blocking_notifier_chain_unregister(&adp5585->event_notifier,
+ &kpad->nb);
+}
+
+static int adp5585_keys_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct adp5585_kpad *kpad;
+ unsigned int revid;
+ const char *phys;
+ int error;
+
+ kpad = devm_kzalloc(dev, sizeof(*kpad), GFP_KERNEL);
+ if (!kpad)
+ return -ENOMEM;
+
+ if (!adp5585->irq)
+ return dev_err_probe(dev, -EINVAL,
+ "IRQ is mandatory for the keypad\n");
+
+ kpad->dev = dev;
+
+ kpad->input = devm_input_allocate_device(dev);
+ if (!kpad->input)
+ return -ENOMEM;
+
+ kpad->info = (const struct adp5585_kpad_chip *)id->driver_data;
+ if (!kpad->info)
+ return -ENODEV;
+
+ error = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
+ if (error)
+ return dev_err_probe(dev, error, "Failed to read device ID\n");
+
+ phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", pdev->name);
+ if (!phys)
+ return -ENOMEM;
+
+ kpad->input->name = pdev->name;
+ kpad->input->phys = phys;
+
+ kpad->input->id.bustype = BUS_I2C;
+ kpad->input->id.vendor = 0x0001;
+ kpad->input->id.product = 0x0001;
+ kpad->input->id.version = revid & ADP5585_REV_ID_MASK;
+
+ device_set_of_node_from_dev(dev, dev->parent);
+
+ error = adp5585_keys_parse_fw(adp5585, kpad);
+ if (error)
+ return error;
+
+ error = adp5585_keys_setup(adp5585, kpad);
+ if (error)
+ return error;
+
+ kpad->nb.notifier_call = adp5585_keys_ev_handle;
+ error = blocking_notifier_chain_register(&adp5585->event_notifier,
+ &kpad->nb);
+ if (error)
+ return error;
+
+ error = devm_add_action_or_reset(dev, adp5585_keys_unreg_notifier, kpad);
+ if (error)
+ return error;
+
+ error = input_register_device(kpad->input);
+ if (error)
+ return dev_err_probe(dev, error,
+ "Failed to register input device\n");
+
+ return 0;
+}
+
+static const struct adp5585_kpad_chip adp5585_kpad_chip_info = {
+ .max_rows = 6,
+ .max_cols = 5,
+ .key_ev_min = ADP5585_ROW5_KEY_EVENT_START,
+ .key_ev_max = ADP5585_ROW5_KEY_EVENT_END,
+};
+
+static const struct adp5585_kpad_chip adp5589_kpad_chip_info = {
+ .max_rows = 8,
+ .max_cols = 11,
+ .key_ev_min = ADP5589_KEY_EVENT_START,
+ .key_ev_max = ADP5589_KEY_EVENT_END,
+};
+
+static const struct platform_device_id adp5585_keys_id_table[] = {
+ { "adp5585-keys", (kernel_ulong_t)&adp5585_kpad_chip_info },
+ { "adp5589-keys", (kernel_ulong_t)&adp5589_kpad_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, adp5585_keys_id_table);
+
+static struct platform_driver adp5585_keys_driver = {
+ .driver = {
+ .name = "adp5585-keys",
+ },
+ .probe = adp5585_keys_probe,
+ .id_table = adp5585_keys_id_table,
+};
+module_platform_driver(adp5585_keys_driver);
+
+MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADP5585 Keys Driver");
+MODULE_LICENSE("GPL");
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 17/20] Input: adp5589: remove the driver
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (15 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 16/20] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 18/20] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
` (2 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
The adp5589 support is based on legacy platform data and there's no
upstream pataform using this device.
Moreover, recently, with
commit
480a8ad683d7 ("mfd: adp5585: Add Analog Devices ADP5585 core support")
we overlapped support for the adp5585 device (gpiochip part of it) but
since it actually makes sense for the device to be supported under MFD, we
can complement it and add the keymap support for it (properly based on FW
properties). And that is what
commit
04840c5363a6 ("Input: adp5585: Add Analog Devices ADP5585/89 support")
is doing.
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/input/keyboard/Kconfig | 10 -
drivers/input/keyboard/Makefile | 1 -
drivers/input/keyboard/adp5589-keys.c | 1066 ---------------------------------
3 files changed, 1077 deletions(-)
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 322da0957067db77c7f66ab26a181d39c2c1d513..76d3397961fa006de4d5979e134b8c6e7dd52c73 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -61,16 +61,6 @@ config KEYBOARD_ADP5588
To compile this driver as a module, choose M here: the
module will be called adp5588-keys.
-config KEYBOARD_ADP5589
- tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander"
- depends on I2C
- help
- Say Y here if you want to use a ADP5585/ADP5589 attached to your
- system I2C bus.
-
- To compile this driver as a module, choose M here: the
- module will be called adp5589-keys.
-
config KEYBOARD_AMIGA
tristate "Amiga keyboard"
depends on AMIGA
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index f00ec003a59aa28577ae164c0539cc5aff9579fc..8bc20ab2b103b0b75c446e4aa919dad01aa5f405 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
-obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
deleted file mode 100644
index 81d0876ee358ef4b521f3f936dc2ab108bb4cda3..0000000000000000000000000000000000000000
--- a/drivers/input/keyboard/adp5589-keys.c
+++ /dev/null
@@ -1,1066 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Description: keypad driver for ADP5589, ADP5585
- * I2C QWERTY Keypad and IO Expander
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * Copyright (C) 2010-2011 Analog Devices Inc.
- */
-
-#include <linux/bitops.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/workqueue.h>
-#include <linux/errno.h>
-#include <linux/pm.h>
-#include <linux/pm_wakeirq.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-#include <linux/i2c.h>
-#include <linux/gpio/driver.h>
-#include <linux/slab.h>
-
-#include <linux/input/adp5589.h>
-
-/* ADP5589/ADP5585 Common Registers */
-#define ADP5589_5_ID 0x00
-#define ADP5589_5_INT_STATUS 0x01
-#define ADP5589_5_STATUS 0x02
-#define ADP5589_5_FIFO_1 0x03
-#define ADP5589_5_FIFO_2 0x04
-#define ADP5589_5_FIFO_3 0x05
-#define ADP5589_5_FIFO_4 0x06
-#define ADP5589_5_FIFO_5 0x07
-#define ADP5589_5_FIFO_6 0x08
-#define ADP5589_5_FIFO_7 0x09
-#define ADP5589_5_FIFO_8 0x0A
-#define ADP5589_5_FIFO_9 0x0B
-#define ADP5589_5_FIFO_10 0x0C
-#define ADP5589_5_FIFO_11 0x0D
-#define ADP5589_5_FIFO_12 0x0E
-#define ADP5589_5_FIFO_13 0x0F
-#define ADP5589_5_FIFO_14 0x10
-#define ADP5589_5_FIFO_15 0x11
-#define ADP5589_5_FIFO_16 0x12
-#define ADP5589_5_GPI_INT_STAT_A 0x13
-#define ADP5589_5_GPI_INT_STAT_B 0x14
-
-/* ADP5589 Registers */
-#define ADP5589_GPI_INT_STAT_C 0x15
-#define ADP5589_GPI_STATUS_A 0x16
-#define ADP5589_GPI_STATUS_B 0x17
-#define ADP5589_GPI_STATUS_C 0x18
-#define ADP5589_RPULL_CONFIG_A 0x19
-#define ADP5589_RPULL_CONFIG_B 0x1A
-#define ADP5589_RPULL_CONFIG_C 0x1B
-#define ADP5589_RPULL_CONFIG_D 0x1C
-#define ADP5589_RPULL_CONFIG_E 0x1D
-#define ADP5589_GPI_INT_LEVEL_A 0x1E
-#define ADP5589_GPI_INT_LEVEL_B 0x1F
-#define ADP5589_GPI_INT_LEVEL_C 0x20
-#define ADP5589_GPI_EVENT_EN_A 0x21
-#define ADP5589_GPI_EVENT_EN_B 0x22
-#define ADP5589_GPI_EVENT_EN_C 0x23
-#define ADP5589_GPI_INTERRUPT_EN_A 0x24
-#define ADP5589_GPI_INTERRUPT_EN_B 0x25
-#define ADP5589_GPI_INTERRUPT_EN_C 0x26
-#define ADP5589_DEBOUNCE_DIS_A 0x27
-#define ADP5589_DEBOUNCE_DIS_B 0x28
-#define ADP5589_DEBOUNCE_DIS_C 0x29
-#define ADP5589_GPO_DATA_OUT_A 0x2A
-#define ADP5589_GPO_DATA_OUT_B 0x2B
-#define ADP5589_GPO_DATA_OUT_C 0x2C
-#define ADP5589_GPO_OUT_MODE_A 0x2D
-#define ADP5589_GPO_OUT_MODE_B 0x2E
-#define ADP5589_GPO_OUT_MODE_C 0x2F
-#define ADP5589_GPIO_DIRECTION_A 0x30
-#define ADP5589_GPIO_DIRECTION_B 0x31
-#define ADP5589_GPIO_DIRECTION_C 0x32
-#define ADP5589_UNLOCK1 0x33
-#define ADP5589_UNLOCK2 0x34
-#define ADP5589_EXT_LOCK_EVENT 0x35
-#define ADP5589_UNLOCK_TIMERS 0x36
-#define ADP5589_LOCK_CFG 0x37
-#define ADP5589_RESET1_EVENT_A 0x38
-#define ADP5589_RESET1_EVENT_B 0x39
-#define ADP5589_RESET1_EVENT_C 0x3A
-#define ADP5589_RESET2_EVENT_A 0x3B
-#define ADP5589_RESET2_EVENT_B 0x3C
-#define ADP5589_RESET_CFG 0x3D
-#define ADP5589_PWM_OFFT_LOW 0x3E
-#define ADP5589_PWM_OFFT_HIGH 0x3F
-#define ADP5589_PWM_ONT_LOW 0x40
-#define ADP5589_PWM_ONT_HIGH 0x41
-#define ADP5589_PWM_CFG 0x42
-#define ADP5589_CLOCK_DIV_CFG 0x43
-#define ADP5589_LOGIC_1_CFG 0x44
-#define ADP5589_LOGIC_2_CFG 0x45
-#define ADP5589_LOGIC_FF_CFG 0x46
-#define ADP5589_LOGIC_INT_EVENT_EN 0x47
-#define ADP5589_POLL_PTIME_CFG 0x48
-#define ADP5589_PIN_CONFIG_A 0x49
-#define ADP5589_PIN_CONFIG_B 0x4A
-#define ADP5589_PIN_CONFIG_C 0x4B
-#define ADP5589_PIN_CONFIG_D 0x4C
-#define ADP5589_GENERAL_CFG 0x4D
-#define ADP5589_INT_EN 0x4E
-
-/* ADP5585 Registers */
-#define ADP5585_GPI_STATUS_A 0x15
-#define ADP5585_GPI_STATUS_B 0x16
-#define ADP5585_RPULL_CONFIG_A 0x17
-#define ADP5585_RPULL_CONFIG_B 0x18
-#define ADP5585_RPULL_CONFIG_C 0x19
-#define ADP5585_RPULL_CONFIG_D 0x1A
-#define ADP5585_GPI_INT_LEVEL_A 0x1B
-#define ADP5585_GPI_INT_LEVEL_B 0x1C
-#define ADP5585_GPI_EVENT_EN_A 0x1D
-#define ADP5585_GPI_EVENT_EN_B 0x1E
-#define ADP5585_GPI_INTERRUPT_EN_A 0x1F
-#define ADP5585_GPI_INTERRUPT_EN_B 0x20
-#define ADP5585_DEBOUNCE_DIS_A 0x21
-#define ADP5585_DEBOUNCE_DIS_B 0x22
-#define ADP5585_GPO_DATA_OUT_A 0x23
-#define ADP5585_GPO_DATA_OUT_B 0x24
-#define ADP5585_GPO_OUT_MODE_A 0x25
-#define ADP5585_GPO_OUT_MODE_B 0x26
-#define ADP5585_GPIO_DIRECTION_A 0x27
-#define ADP5585_GPIO_DIRECTION_B 0x28
-#define ADP5585_RESET1_EVENT_A 0x29
-#define ADP5585_RESET1_EVENT_B 0x2A
-#define ADP5585_RESET1_EVENT_C 0x2B
-#define ADP5585_RESET2_EVENT_A 0x2C
-#define ADP5585_RESET2_EVENT_B 0x2D
-#define ADP5585_RESET_CFG 0x2E
-#define ADP5585_PWM_OFFT_LOW 0x2F
-#define ADP5585_PWM_OFFT_HIGH 0x30
-#define ADP5585_PWM_ONT_LOW 0x31
-#define ADP5585_PWM_ONT_HIGH 0x32
-#define ADP5585_PWM_CFG 0x33
-#define ADP5585_LOGIC_CFG 0x34
-#define ADP5585_LOGIC_FF_CFG 0x35
-#define ADP5585_LOGIC_INT_EVENT_EN 0x36
-#define ADP5585_POLL_PTIME_CFG 0x37
-#define ADP5585_PIN_CONFIG_A 0x38
-#define ADP5585_PIN_CONFIG_B 0x39
-#define ADP5585_PIN_CONFIG_D 0x3A
-#define ADP5585_GENERAL_CFG 0x3B
-#define ADP5585_INT_EN 0x3C
-
-/* ID Register */
-#define ADP5589_5_DEVICE_ID_MASK 0xF
-#define ADP5589_5_MAN_ID_MASK 0xF
-#define ADP5589_5_MAN_ID_SHIFT 4
-#define ADP5589_5_MAN_ID 0x02
-
-/* GENERAL_CFG Register */
-#define OSC_EN BIT(7)
-#define CORE_CLK(x) (((x) & 0x3) << 5)
-#define LCK_TRK_LOGIC BIT(4) /* ADP5589 only */
-#define LCK_TRK_GPI BIT(3) /* ADP5589 only */
-#define INT_CFG BIT(1)
-#define RST_CFG BIT(0)
-
-/* INT_EN Register */
-#define LOGIC2_IEN BIT(5) /* ADP5589 only */
-#define LOGIC1_IEN BIT(4)
-#define LOCK_IEN BIT(3) /* ADP5589 only */
-#define OVRFLOW_IEN BIT(2)
-#define GPI_IEN BIT(1)
-#define EVENT_IEN BIT(0)
-
-/* Interrupt Status Register */
-#define LOGIC2_INT BIT(5) /* ADP5589 only */
-#define LOGIC1_INT BIT(4)
-#define LOCK_INT BIT(3) /* ADP5589 only */
-#define OVRFLOW_INT BIT(2)
-#define GPI_INT BIT(1)
-#define EVENT_INT BIT(0)
-
-/* STATUS Register */
-#define LOGIC2_STAT BIT(7) /* ADP5589 only */
-#define LOGIC1_STAT BIT(6)
-#define LOCK_STAT BIT(5) /* ADP5589 only */
-#define KEC 0x1F
-
-/* PIN_CONFIG_D Register */
-#define C4_EXTEND_CFG BIT(6) /* RESET2 */
-#define R4_EXTEND_CFG BIT(5) /* RESET1 */
-
-/* LOCK_CFG */
-#define LOCK_EN BIT(0)
-
-#define PTIME_MASK 0x3
-#define LTIME_MASK 0x3 /* ADP5589 only */
-
-/* Key Event Register xy */
-#define KEY_EV_PRESSED BIT(7)
-#define KEY_EV_MASK 0x7F
-
-#define KEYP_MAX_EVENT 16
-#define ADP5589_MAXGPIO 19
-#define ADP5585_MAXGPIO 11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */
-
-enum {
- ADP5589,
- ADP5585_01,
- ADP5585_02
-};
-
-struct adp_constants {
- u8 maxgpio;
- u8 keymapsize;
- u8 gpi_pin_row_base;
- u8 gpi_pin_row_end;
- u8 gpi_pin_col_base;
- u8 gpi_pin_base;
- u8 gpi_pin_end;
- u8 gpimapsize_max;
- u8 max_row_num;
- u8 max_col_num;
- u8 row_mask;
- u8 col_mask;
- u8 col_shift;
- u8 c4_extend_cfg;
- u8 (*bank) (u8 offset);
- u8 (*bit) (u8 offset);
- u8 (*reg) (u8 reg);
-};
-
-struct adp5589_kpad {
- struct i2c_client *client;
- struct input_dev *input;
- const struct adp_constants *var;
- unsigned short keycode[ADP5589_KEYMAPSIZE];
- const struct adp5589_gpi_map *gpimap;
- unsigned short gpimapsize;
- unsigned extend_cfg;
- bool is_adp5585;
- bool support_row5;
-#ifdef CONFIG_GPIOLIB
- unsigned char gpiomap[ADP5589_MAXGPIO];
- struct gpio_chip gc;
- struct mutex gpio_lock; /* Protect cached dir, dat_out */
- u8 dat_out[3];
- u8 dir[3];
-#endif
-};
-
-/*
- * ADP5589 / ADP5585 derivative / variant handling
- */
-
-
-/* ADP5589 */
-
-static unsigned char adp5589_bank(unsigned char offset)
-{
- return offset >> 3;
-}
-
-static unsigned char adp5589_bit(unsigned char offset)
-{
- return 1u << (offset & 0x7);
-}
-
-static unsigned char adp5589_reg(unsigned char reg)
-{
- return reg;
-}
-
-static const struct adp_constants const_adp5589 = {
- .maxgpio = ADP5589_MAXGPIO,
- .keymapsize = ADP5589_KEYMAPSIZE,
- .gpi_pin_row_base = ADP5589_GPI_PIN_ROW_BASE,
- .gpi_pin_row_end = ADP5589_GPI_PIN_ROW_END,
- .gpi_pin_col_base = ADP5589_GPI_PIN_COL_BASE,
- .gpi_pin_base = ADP5589_GPI_PIN_BASE,
- .gpi_pin_end = ADP5589_GPI_PIN_END,
- .gpimapsize_max = ADP5589_GPIMAPSIZE_MAX,
- .c4_extend_cfg = 12,
- .max_row_num = ADP5589_MAX_ROW_NUM,
- .max_col_num = ADP5589_MAX_COL_NUM,
- .row_mask = ADP5589_ROW_MASK,
- .col_mask = ADP5589_COL_MASK,
- .col_shift = ADP5589_COL_SHIFT,
- .bank = adp5589_bank,
- .bit = adp5589_bit,
- .reg = adp5589_reg,
-};
-
-/* ADP5585 */
-
-static unsigned char adp5585_bank(unsigned char offset)
-{
- return offset > ADP5585_MAX_ROW_NUM;
-}
-
-static unsigned char adp5585_bit(unsigned char offset)
-{
- return (offset > ADP5585_MAX_ROW_NUM) ?
- 1u << (offset - ADP5585_COL_SHIFT) : 1u << offset;
-}
-
-static const unsigned char adp5585_reg_lut[] = {
- [ADP5589_GPI_STATUS_A] = ADP5585_GPI_STATUS_A,
- [ADP5589_GPI_STATUS_B] = ADP5585_GPI_STATUS_B,
- [ADP5589_RPULL_CONFIG_A] = ADP5585_RPULL_CONFIG_A,
- [ADP5589_RPULL_CONFIG_B] = ADP5585_RPULL_CONFIG_B,
- [ADP5589_RPULL_CONFIG_C] = ADP5585_RPULL_CONFIG_C,
- [ADP5589_RPULL_CONFIG_D] = ADP5585_RPULL_CONFIG_D,
- [ADP5589_GPI_INT_LEVEL_A] = ADP5585_GPI_INT_LEVEL_A,
- [ADP5589_GPI_INT_LEVEL_B] = ADP5585_GPI_INT_LEVEL_B,
- [ADP5589_GPI_EVENT_EN_A] = ADP5585_GPI_EVENT_EN_A,
- [ADP5589_GPI_EVENT_EN_B] = ADP5585_GPI_EVENT_EN_B,
- [ADP5589_GPI_INTERRUPT_EN_A] = ADP5585_GPI_INTERRUPT_EN_A,
- [ADP5589_GPI_INTERRUPT_EN_B] = ADP5585_GPI_INTERRUPT_EN_B,
- [ADP5589_DEBOUNCE_DIS_A] = ADP5585_DEBOUNCE_DIS_A,
- [ADP5589_DEBOUNCE_DIS_B] = ADP5585_DEBOUNCE_DIS_B,
- [ADP5589_GPO_DATA_OUT_A] = ADP5585_GPO_DATA_OUT_A,
- [ADP5589_GPO_DATA_OUT_B] = ADP5585_GPO_DATA_OUT_B,
- [ADP5589_GPO_OUT_MODE_A] = ADP5585_GPO_OUT_MODE_A,
- [ADP5589_GPO_OUT_MODE_B] = ADP5585_GPO_OUT_MODE_B,
- [ADP5589_GPIO_DIRECTION_A] = ADP5585_GPIO_DIRECTION_A,
- [ADP5589_GPIO_DIRECTION_B] = ADP5585_GPIO_DIRECTION_B,
- [ADP5589_RESET1_EVENT_A] = ADP5585_RESET1_EVENT_A,
- [ADP5589_RESET1_EVENT_B] = ADP5585_RESET1_EVENT_B,
- [ADP5589_RESET1_EVENT_C] = ADP5585_RESET1_EVENT_C,
- [ADP5589_RESET2_EVENT_A] = ADP5585_RESET2_EVENT_A,
- [ADP5589_RESET2_EVENT_B] = ADP5585_RESET2_EVENT_B,
- [ADP5589_RESET_CFG] = ADP5585_RESET_CFG,
- [ADP5589_PWM_OFFT_LOW] = ADP5585_PWM_OFFT_LOW,
- [ADP5589_PWM_OFFT_HIGH] = ADP5585_PWM_OFFT_HIGH,
- [ADP5589_PWM_ONT_LOW] = ADP5585_PWM_ONT_LOW,
- [ADP5589_PWM_ONT_HIGH] = ADP5585_PWM_ONT_HIGH,
- [ADP5589_PWM_CFG] = ADP5585_PWM_CFG,
- [ADP5589_LOGIC_1_CFG] = ADP5585_LOGIC_CFG,
- [ADP5589_LOGIC_FF_CFG] = ADP5585_LOGIC_FF_CFG,
- [ADP5589_LOGIC_INT_EVENT_EN] = ADP5585_LOGIC_INT_EVENT_EN,
- [ADP5589_POLL_PTIME_CFG] = ADP5585_POLL_PTIME_CFG,
- [ADP5589_PIN_CONFIG_A] = ADP5585_PIN_CONFIG_A,
- [ADP5589_PIN_CONFIG_B] = ADP5585_PIN_CONFIG_B,
- [ADP5589_PIN_CONFIG_D] = ADP5585_PIN_CONFIG_D,
- [ADP5589_GENERAL_CFG] = ADP5585_GENERAL_CFG,
- [ADP5589_INT_EN] = ADP5585_INT_EN,
-};
-
-static unsigned char adp5585_reg(unsigned char reg)
-{
- return adp5585_reg_lut[reg];
-}
-
-static const struct adp_constants const_adp5585 = {
- .maxgpio = ADP5585_MAXGPIO,
- .keymapsize = ADP5585_KEYMAPSIZE,
- .gpi_pin_row_base = ADP5585_GPI_PIN_ROW_BASE,
- .gpi_pin_row_end = ADP5585_GPI_PIN_ROW_END,
- .gpi_pin_col_base = ADP5585_GPI_PIN_COL_BASE,
- .gpi_pin_base = ADP5585_GPI_PIN_BASE,
- .gpi_pin_end = ADP5585_GPI_PIN_END,
- .gpimapsize_max = ADP5585_GPIMAPSIZE_MAX,
- .c4_extend_cfg = 10,
- .max_row_num = ADP5585_MAX_ROW_NUM,
- .max_col_num = ADP5585_MAX_COL_NUM,
- .row_mask = ADP5585_ROW_MASK,
- .col_mask = ADP5585_COL_MASK,
- .col_shift = ADP5585_COL_SHIFT,
- .bank = adp5585_bank,
- .bit = adp5585_bit,
- .reg = adp5585_reg,
-};
-
-static int adp5589_read(struct i2c_client *client, u8 reg)
-{
- int ret = i2c_smbus_read_byte_data(client, reg);
-
- if (ret < 0)
- dev_err(&client->dev, "Read Error\n");
-
- return ret;
-}
-
-static int adp5589_write(struct i2c_client *client, u8 reg, u8 val)
-{
- return i2c_smbus_write_byte_data(client, reg, val);
-}
-
-#ifdef CONFIG_GPIOLIB
-static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
-{
- struct adp5589_kpad *kpad = gpiochip_get_data(chip);
- unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
- unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
- int val;
-
- mutex_lock(&kpad->gpio_lock);
- if (kpad->dir[bank] & bit)
- val = kpad->dat_out[bank];
- else
- val = adp5589_read(kpad->client,
- kpad->var->reg(ADP5589_GPI_STATUS_A) + bank);
- mutex_unlock(&kpad->gpio_lock);
-
- return !!(val & bit);
-}
-
-static void adp5589_gpio_set_value(struct gpio_chip *chip,
- unsigned off, int val)
-{
- struct adp5589_kpad *kpad = gpiochip_get_data(chip);
- unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
- unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
-
- guard(mutex)(&kpad->gpio_lock);
-
- if (val)
- kpad->dat_out[bank] |= bit;
- else
- kpad->dat_out[bank] &= ~bit;
-
- adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) +
- bank, kpad->dat_out[bank]);
-}
-
-static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
-{
- struct adp5589_kpad *kpad = gpiochip_get_data(chip);
- unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
- unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
-
- guard(mutex)(&kpad->gpio_lock);
-
- kpad->dir[bank] &= ~bit;
- return adp5589_write(kpad->client,
- kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
- kpad->dir[bank]);
-}
-
-static int adp5589_gpio_direction_output(struct gpio_chip *chip,
- unsigned off, int val)
-{
- struct adp5589_kpad *kpad = gpiochip_get_data(chip);
- unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
- unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
- int error;
-
- guard(mutex)(&kpad->gpio_lock);
-
- kpad->dir[bank] |= bit;
-
- if (val)
- kpad->dat_out[bank] |= bit;
- else
- kpad->dat_out[bank] &= ~bit;
-
- error = adp5589_write(kpad->client,
- kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank,
- kpad->dat_out[bank]);
- if (error)
- return error;
-
- error = adp5589_write(kpad->client,
- kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
- kpad->dir[bank]);
- if (error)
- return error;
-
- return 0;
-}
-
-static int adp5589_build_gpiomap(struct adp5589_kpad *kpad,
- const struct adp5589_kpad_platform_data *pdata)
-{
- bool pin_used[ADP5589_MAXGPIO];
- int n_unused = 0;
- int i;
-
- memset(pin_used, false, sizeof(pin_used));
-
- for (i = 0; i < kpad->var->maxgpio; i++)
- if (pdata->keypad_en_mask & BIT(i))
- pin_used[i] = true;
-
- for (i = 0; i < kpad->gpimapsize; i++)
- pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true;
-
- if (kpad->extend_cfg & R4_EXTEND_CFG)
- pin_used[4] = true;
-
- if (kpad->extend_cfg & C4_EXTEND_CFG)
- pin_used[kpad->var->c4_extend_cfg] = true;
-
- if (!kpad->support_row5)
- pin_used[5] = true;
-
- for (i = 0; i < kpad->var->maxgpio; i++)
- if (!pin_used[i])
- kpad->gpiomap[n_unused++] = i;
-
- return n_unused;
-}
-
-static int adp5589_gpio_add(struct adp5589_kpad *kpad)
-{
- struct device *dev = &kpad->client->dev;
- const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev);
- const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
- int i, error;
-
- if (!gpio_data)
- return 0;
-
- kpad->gc.parent = dev;
- kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata);
- if (kpad->gc.ngpio == 0) {
- dev_info(dev, "No unused gpios left to export\n");
- return 0;
- }
-
- kpad->gc.direction_input = adp5589_gpio_direction_input;
- kpad->gc.direction_output = adp5589_gpio_direction_output;
- kpad->gc.get = adp5589_gpio_get_value;
- kpad->gc.set = adp5589_gpio_set_value;
- kpad->gc.can_sleep = 1;
-
- kpad->gc.base = gpio_data->gpio_start;
- kpad->gc.label = kpad->client->name;
- kpad->gc.owner = THIS_MODULE;
-
- mutex_init(&kpad->gpio_lock);
-
- error = devm_gpiochip_add_data(dev, &kpad->gc, kpad);
- if (error)
- return error;
-
- for (i = 0; i <= kpad->var->bank(kpad->var->maxgpio); i++) {
- kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg(
- ADP5589_GPO_DATA_OUT_A) + i);
- kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg(
- ADP5589_GPIO_DIRECTION_A) + i);
- }
-
- return 0;
-}
-#else
-static inline int adp5589_gpio_add(struct adp5589_kpad *kpad)
-{
- return 0;
-}
-#endif
-
-static void adp5589_report_switches(struct adp5589_kpad *kpad,
- int key, int key_val)
-{
- int i;
-
- for (i = 0; i < kpad->gpimapsize; i++) {
- if (key_val == kpad->gpimap[i].pin) {
- input_report_switch(kpad->input,
- kpad->gpimap[i].sw_evt,
- key & KEY_EV_PRESSED);
- break;
- }
- }
-}
-
-static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt)
-{
- int i;
-
- for (i = 0; i < ev_cnt; i++) {
- int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i);
- int key_val = key & KEY_EV_MASK;
-
- if (key_val >= kpad->var->gpi_pin_base &&
- key_val <= kpad->var->gpi_pin_end) {
- adp5589_report_switches(kpad, key, key_val);
- } else {
- input_report_key(kpad->input,
- kpad->keycode[key_val - 1],
- key & KEY_EV_PRESSED);
- }
- }
-}
-
-static irqreturn_t adp5589_irq(int irq, void *handle)
-{
- struct adp5589_kpad *kpad = handle;
- struct i2c_client *client = kpad->client;
- int status, ev_cnt;
-
- status = adp5589_read(client, ADP5589_5_INT_STATUS);
-
- if (status & OVRFLOW_INT) /* Unlikely and should never happen */
- dev_err(&client->dev, "Event Overflow Error\n");
-
- if (status & EVENT_INT) {
- ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC;
- if (ev_cnt) {
- adp5589_report_events(kpad, ev_cnt);
- input_sync(kpad->input);
- }
- }
-
- adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */
-
- return IRQ_HANDLED;
-}
-
-static int adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key)
-{
- int i;
-
- for (i = 0; i < kpad->var->keymapsize; i++)
- if (key == kpad->keycode[i])
- return (i + 1) | KEY_EV_PRESSED;
-
- dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n");
-
- return -EINVAL;
-}
-
-static int adp5589_setup(struct adp5589_kpad *kpad)
-{
- struct i2c_client *client = kpad->client;
- const struct adp5589_kpad_platform_data *pdata =
- dev_get_platdata(&client->dev);
- u8 (*reg) (u8) = kpad->var->reg;
- unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
- unsigned char pull_mask = 0;
- int i, ret;
-
- ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A),
- pdata->keypad_en_mask & kpad->var->row_mask);
- ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B),
- (pdata->keypad_en_mask >> kpad->var->col_shift) &
- kpad->var->col_mask);
-
- if (!kpad->is_adp5585)
- ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
- (pdata->keypad_en_mask >> 16) & 0xFF);
-
- if (!kpad->is_adp5585 && pdata->en_keylock) {
- ret |= adp5589_write(client, ADP5589_UNLOCK1,
- pdata->unlock_key1);
- ret |= adp5589_write(client, ADP5589_UNLOCK2,
- pdata->unlock_key2);
- ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS,
- pdata->unlock_timer & LTIME_MASK);
- ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN);
- }
-
- for (i = 0; i < KEYP_MAX_EVENT; i++)
- ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i);
-
- for (i = 0; i < pdata->gpimapsize; i++) {
- unsigned short pin = pdata->gpimap[i].pin;
-
- if (pin <= kpad->var->gpi_pin_row_end) {
- evt_mode1 |= BIT(pin - kpad->var->gpi_pin_row_base);
- } else {
- evt_mode2 |=
- BIT(pin - kpad->var->gpi_pin_col_base) & 0xFF;
- if (!kpad->is_adp5585)
- evt_mode3 |=
- BIT(pin - kpad->var->gpi_pin_col_base) >> 8;
- }
- }
-
- if (pdata->gpimapsize) {
- ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A),
- evt_mode1);
- ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B),
- evt_mode2);
- if (!kpad->is_adp5585)
- ret |= adp5589_write(client,
- reg(ADP5589_GPI_EVENT_EN_C),
- evt_mode3);
- }
-
- if (pdata->pull_dis_mask & pdata->pullup_en_100k &
- pdata->pullup_en_300k & pdata->pulldown_en_300k)
- dev_warn(&client->dev, "Conflicting pull resistor config\n");
-
- for (i = 0; i <= kpad->var->max_row_num; i++) {
- unsigned int val = 0, bit = BIT(i);
- if (pdata->pullup_en_300k & bit)
- val = 0;
- else if (pdata->pulldown_en_300k & bit)
- val = 1;
- else if (pdata->pullup_en_100k & bit)
- val = 2;
- else if (pdata->pull_dis_mask & bit)
- val = 3;
-
- pull_mask |= val << (2 * (i & 0x3));
-
- if (i % 4 == 3 || i == kpad->var->max_row_num) {
- ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A)
- + (i >> 2), pull_mask);
- pull_mask = 0;
- }
- }
-
- for (i = 0; i <= kpad->var->max_col_num; i++) {
- unsigned int val = 0, bit = BIT(i + kpad->var->col_shift);
- if (pdata->pullup_en_300k & bit)
- val = 0;
- else if (pdata->pulldown_en_300k & bit)
- val = 1;
- else if (pdata->pullup_en_100k & bit)
- val = 2;
- else if (pdata->pull_dis_mask & bit)
- val = 3;
-
- pull_mask |= val << (2 * (i & 0x3));
-
- if (i % 4 == 3 || i == kpad->var->max_col_num) {
- ret |= adp5589_write(client,
- reg(ADP5585_RPULL_CONFIG_C) +
- (i >> 2), pull_mask);
- pull_mask = 0;
- }
- }
-
- if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) {
- ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A),
- adp5589_get_evcode(kpad,
- pdata->reset1_key_1));
- ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B),
- adp5589_get_evcode(kpad,
- pdata->reset1_key_2));
- ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C),
- adp5589_get_evcode(kpad,
- pdata->reset1_key_3));
- kpad->extend_cfg |= R4_EXTEND_CFG;
- }
-
- if (pdata->reset2_key_1 && pdata->reset2_key_2) {
- ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A),
- adp5589_get_evcode(kpad,
- pdata->reset2_key_1));
- ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B),
- adp5589_get_evcode(kpad,
- pdata->reset2_key_2));
- kpad->extend_cfg |= C4_EXTEND_CFG;
- }
-
- if (kpad->extend_cfg) {
- ret |= adp5589_write(client, reg(ADP5589_RESET_CFG),
- pdata->reset_cfg);
- ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D),
- kpad->extend_cfg);
- }
-
- ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A),
- pdata->debounce_dis_mask & kpad->var->row_mask);
-
- ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B),
- (pdata->debounce_dis_mask >> kpad->var->col_shift)
- & kpad->var->col_mask);
-
- if (!kpad->is_adp5585)
- ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C),
- (pdata->debounce_dis_mask >> 16) & 0xFF);
-
- ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG),
- pdata->scan_cycle_time & PTIME_MASK);
- ret |= adp5589_write(client, ADP5589_5_INT_STATUS,
- (kpad->is_adp5585 ? 0 : LOGIC2_INT) |
- LOGIC1_INT | OVRFLOW_INT |
- (kpad->is_adp5585 ? 0 : LOCK_INT) |
- GPI_INT | EVENT_INT); /* Status is W1C */
-
- ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG),
- INT_CFG | OSC_EN | CORE_CLK(3));
- ret |= adp5589_write(client, reg(ADP5589_INT_EN),
- OVRFLOW_IEN | GPI_IEN | EVENT_IEN);
-
- if (ret < 0) {
- dev_err(&client->dev, "Write Error\n");
- return ret;
- }
-
- return 0;
-}
-
-static void adp5589_report_switch_state(struct adp5589_kpad *kpad)
-{
- int gpi_stat_tmp, pin_loc;
- int i;
- int gpi_stat1 = adp5589_read(kpad->client,
- kpad->var->reg(ADP5589_GPI_STATUS_A));
- int gpi_stat2 = adp5589_read(kpad->client,
- kpad->var->reg(ADP5589_GPI_STATUS_B));
- int gpi_stat3 = !kpad->is_adp5585 ?
- adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0;
-
- for (i = 0; i < kpad->gpimapsize; i++) {
- unsigned short pin = kpad->gpimap[i].pin;
-
- if (pin <= kpad->var->gpi_pin_row_end) {
- gpi_stat_tmp = gpi_stat1;
- pin_loc = pin - kpad->var->gpi_pin_row_base;
- } else if ((pin - kpad->var->gpi_pin_col_base) < 8) {
- gpi_stat_tmp = gpi_stat2;
- pin_loc = pin - kpad->var->gpi_pin_col_base;
- } else {
- gpi_stat_tmp = gpi_stat3;
- pin_loc = pin - kpad->var->gpi_pin_col_base - 8;
- }
-
- if (gpi_stat_tmp < 0) {
- dev_err(&kpad->client->dev,
- "Can't read GPIO_DAT_STAT switch %d, default to OFF\n",
- pin);
- gpi_stat_tmp = 0;
- }
-
- input_report_switch(kpad->input,
- kpad->gpimap[i].sw_evt,
- !(gpi_stat_tmp & BIT(pin_loc)));
- }
-
- input_sync(kpad->input);
-}
-
-static int adp5589_keypad_add(struct adp5589_kpad *kpad, unsigned int revid)
-{
- struct i2c_client *client = kpad->client;
- const struct adp5589_kpad_platform_data *pdata =
- dev_get_platdata(&client->dev);
- struct input_dev *input;
- unsigned int i;
- int error;
-
- if (!((pdata->keypad_en_mask & kpad->var->row_mask) &&
- (pdata->keypad_en_mask >> kpad->var->col_shift)) ||
- !pdata->keymap) {
- dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
- return -EINVAL;
- }
-
- if (pdata->keymapsize != kpad->var->keymapsize) {
- dev_err(&client->dev, "invalid keymapsize\n");
- return -EINVAL;
- }
-
- if (!pdata->gpimap && pdata->gpimapsize) {
- dev_err(&client->dev, "invalid gpimap from pdata\n");
- return -EINVAL;
- }
-
- if (pdata->gpimapsize > kpad->var->gpimapsize_max) {
- dev_err(&client->dev, "invalid gpimapsize\n");
- return -EINVAL;
- }
-
- for (i = 0; i < pdata->gpimapsize; i++) {
- unsigned short pin = pdata->gpimap[i].pin;
-
- if (pin < kpad->var->gpi_pin_base ||
- pin > kpad->var->gpi_pin_end) {
- dev_err(&client->dev, "invalid gpi pin data\n");
- return -EINVAL;
- }
-
- if (BIT(pin - kpad->var->gpi_pin_row_base) &
- pdata->keypad_en_mask) {
- dev_err(&client->dev, "invalid gpi row/col data\n");
- return -EINVAL;
- }
- }
-
- if (!client->irq) {
- dev_err(&client->dev, "no IRQ?\n");
- return -EINVAL;
- }
-
- input = devm_input_allocate_device(&client->dev);
- if (!input)
- return -ENOMEM;
-
- kpad->input = input;
-
- input->name = client->name;
- input->phys = "adp5589-keys/input0";
- input->dev.parent = &client->dev;
-
- input_set_drvdata(input, kpad);
-
- input->id.bustype = BUS_I2C;
- input->id.vendor = 0x0001;
- input->id.product = 0x0001;
- input->id.version = revid;
-
- input->keycodesize = sizeof(kpad->keycode[0]);
- input->keycodemax = pdata->keymapsize;
- input->keycode = kpad->keycode;
-
- memcpy(kpad->keycode, pdata->keymap,
- pdata->keymapsize * input->keycodesize);
-
- kpad->gpimap = pdata->gpimap;
- kpad->gpimapsize = pdata->gpimapsize;
-
- /* setup input device */
- __set_bit(EV_KEY, input->evbit);
-
- if (pdata->repeat)
- __set_bit(EV_REP, input->evbit);
-
- for (i = 0; i < input->keycodemax; i++)
- if (kpad->keycode[i] <= KEY_MAX)
- __set_bit(kpad->keycode[i], input->keybit);
- __clear_bit(KEY_RESERVED, input->keybit);
-
- if (kpad->gpimapsize)
- __set_bit(EV_SW, input->evbit);
- for (i = 0; i < kpad->gpimapsize; i++)
- __set_bit(kpad->gpimap[i].sw_evt, input->swbit);
-
- error = input_register_device(input);
- if (error) {
- dev_err(&client->dev, "unable to register input device\n");
- return error;
- }
-
- error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, adp5589_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- client->dev.driver->name, kpad);
- if (error) {
- dev_err(&client->dev, "unable to request irq %d\n", client->irq);
- return error;
- }
-
- return 0;
-}
-
-static void adp5589_clear_config(void *data)
-{
- struct adp5589_kpad *kpad = data;
-
- adp5589_write(kpad->client, kpad->var->reg(ADP5589_GENERAL_CFG), 0);
-}
-
-static int adp5589_probe(struct i2c_client *client)
-{
- const struct i2c_device_id *id = i2c_client_get_device_id(client);
- struct adp5589_kpad *kpad;
- const struct adp5589_kpad_platform_data *pdata =
- dev_get_platdata(&client->dev);
- unsigned int revid;
- int error, ret;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
- return -EIO;
- }
-
- if (!pdata) {
- dev_err(&client->dev, "no platform data?\n");
- return -EINVAL;
- }
-
- kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL);
- if (!kpad)
- return -ENOMEM;
-
- kpad->client = client;
-
- switch (id->driver_data) {
- case ADP5585_02:
- kpad->support_row5 = true;
- fallthrough;
- case ADP5585_01:
- kpad->is_adp5585 = true;
- kpad->var = &const_adp5585;
- break;
- case ADP5589:
- kpad->support_row5 = true;
- kpad->var = &const_adp5589;
- break;
- }
-
- error = devm_add_action_or_reset(&client->dev, adp5589_clear_config,
- kpad);
- if (error)
- return error;
-
- ret = adp5589_read(client, ADP5589_5_ID);
- if (ret < 0)
- return ret;
-
- revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK;
-
- if (pdata->keymapsize) {
- error = adp5589_keypad_add(kpad, revid);
- if (error)
- return error;
- }
-
- error = adp5589_setup(kpad);
- if (error)
- return error;
-
- if (kpad->gpimapsize)
- adp5589_report_switch_state(kpad);
-
- error = adp5589_gpio_add(kpad);
- if (error)
- return error;
-
- dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
- return 0;
-}
-
-static int adp5589_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adp5589_kpad *kpad = i2c_get_clientdata(client);
-
- if (kpad->input)
- disable_irq(client->irq);
-
- return 0;
-}
-
-static int adp5589_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adp5589_kpad *kpad = i2c_get_clientdata(client);
-
- if (kpad->input)
- enable_irq(client->irq);
-
- return 0;
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume);
-
-static const struct i2c_device_id adp5589_id[] = {
- {"adp5589-keys", ADP5589},
- {"adp5585-keys", ADP5585_01},
- {"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, adp5589_id);
-
-static struct i2c_driver adp5589_driver = {
- .driver = {
- .name = KBUILD_MODNAME,
- .pm = pm_sleep_ptr(&adp5589_dev_pm_ops),
- },
- .probe = adp5589_probe,
- .id_table = adp5589_id,
-};
-
-module_i2c_driver(adp5589_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver");
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 18/20] mfd: adp5585: support getting vdd regulator
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (16 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 17/20] Input: adp5589: remove the driver Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-06-12 15:17 ` Lee Jones
2025-05-21 13:03 ` [PATCH v4 19/20] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 20/20] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Make sure we get and enable the VDD supply (if available).
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 122e2c95385f8d5cbd7839db78dda77ad7ba4ae4..e8b9a0ef4ee654ac1abc4042152fe0933f1d9f0d 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -17,6 +17,7 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/types.h>
enum {
@@ -713,6 +714,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
+ ret = devm_regulator_get_enable(&i2c->dev, "vdd");
+ if (ret)
+ return ret;
+
adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
if (IS_ERR(adp5585->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 18/20] mfd: adp5585: support getting vdd regulator
2025-05-21 13:03 ` [PATCH v4 18/20] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
@ 2025-06-12 15:17 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 15:17 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Make sure we get and enable the VDD supply (if available).
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 5 +++++
> 1 file changed, 5 insertions(+)
Reviewed-by: Lee Jones <lee@kernel.org>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 122e2c95385f8d5cbd7839db78dda77ad7ba4ae4..e8b9a0ef4ee654ac1abc4042152fe0933f1d9f0d 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -17,6 +17,7 @@
> #include <linux/mod_devicetable.h>
> #include <linux/module.h>
> #include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> #include <linux/types.h>
>
> enum {
> @@ -713,6 +714,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> if (ret)
> return ret;
>
> + ret = devm_regulator_get_enable(&i2c->dev, "vdd");
> + if (ret)
> + return ret;
> +
> adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 19/20] dt-bindings: mfd: adp5585: document reset gpio
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (17 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 18/20] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-05-21 13:03 ` [PATCH v4 20/20] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
19 siblings, 0 replies; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying, Krzysztof Kozlowski
From: Nuno Sá <nuno.sa@analog.com>
Add a reset gpio property. Note that for the adp5585-01 models, the
reset pin is used as the additional ROW5 which means there's no reset.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
Documentation/devicetree/bindings/mfd/adi,adp5585.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
index b3bf2ed586104303fd078bd06683e4f0d3383575..2d4ecee3f2547ad07a0ab8fcbe96f42f526d1619 100644
--- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
+++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml
@@ -39,6 +39,9 @@ properties:
vdd-supply: true
+ reset-gpios:
+ maxItems: 1
+
gpio-controller: true
'#gpio-cells':
@@ -166,6 +169,7 @@ allOf:
adi,unlock-events: false
adi,unlock-trigger-sec: false
gpio-reserved-ranges: false
+ reset-gpios: false
adi,keypad-pins:
minItems: 2
maxItems: 11
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v4 20/20] mfd: adp5585: add support for a reset pin
2025-05-21 13:02 [PATCH v4 00/20] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (18 preceding siblings ...)
2025-05-21 13:03 ` [PATCH v4 19/20] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
@ 2025-05-21 13:03 ` Nuno Sá via B4 Relay
2025-06-12 15:18 ` Lee Jones
19 siblings, 1 reply; 45+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-21 13:03 UTC (permalink / raw)
To: linux-gpio, linux-pwm, devicetree, linux-input
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Laurent Pinchart, Liu Ying
From: Nuno Sá <nuno.sa@analog.com>
Make sure to perform an Hardware reset during probe if the pin is given
in FW.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index e8b9a0ef4ee654ac1abc4042152fe0933f1d9f0d..164da0b804c4fce4e3af1ac0c2813020a77f4938 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
#include <linux/mfd/adp5585.h>
#include <linux/mfd/core.h>
#include <linux/mod_devicetable.h>
@@ -697,6 +698,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config regmap_config;
struct adp5585_dev *adp5585;
+ struct gpio_desc *gpio;
unsigned int id;
int ret;
@@ -718,6 +720,20 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
+ gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+
+ /*
+ * Note the timings are not documented anywhere in the DS. They are just
+ * reasonable values that work.
+ */
+ if (gpio) {
+ fsleep(30);
+ gpiod_set_value_cansleep(gpio, 0);
+ fsleep(60);
+ }
+
adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
if (IS_ERR(adp5585->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
2.49.0
^ permalink raw reply related [flat|nested] 45+ messages in thread
* Re: [PATCH v4 20/20] mfd: adp5585: add support for a reset pin
2025-05-21 13:03 ` [PATCH v4 20/20] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
@ 2025-06-12 15:18 ` Lee Jones
0 siblings, 0 replies; 45+ messages in thread
From: Lee Jones @ 2025-06-12 15:18 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Wed, 21 May 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Make sure to perform an Hardware reset during probe if the pin is given
> in FW.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index e8b9a0ef4ee654ac1abc4042152fe0933f1d9f0d..164da0b804c4fce4e3af1ac0c2813020a77f4938 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -12,6 +12,7 @@
> #include <linux/device.h>
> #include <linux/err.h>
> #include <linux/i2c.h>
> +#include <linux/gpio/consumer.h>
> #include <linux/mfd/adp5585.h>
> #include <linux/mfd/core.h>
> #include <linux/mod_devicetable.h>
> @@ -697,6 +698,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> struct regmap_config regmap_config;
> struct adp5585_dev *adp5585;
> + struct gpio_desc *gpio;
> unsigned int id;
> int ret;
>
> @@ -718,6 +720,20 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> if (ret)
> return ret;
>
> + gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(gpio))
> + return PTR_ERR(gpio);
> +
> + /*
> + * Note the timings are not documented anywhere in the DS. They are just
"datasheet"
> + * reasonable values that work.
> + */
> + if (gpio) {
> + fsleep(30);
> + gpiod_set_value_cansleep(gpio, 0);
> + fsleep(60);
> + }
> +
> adp5585->regmap = devm_regmap_init_i2c(i2c, ®map_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 45+ messages in thread