linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver
@ 2025-05-12 12:38 Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 01/22] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
                   ` (23 more replies)
  0 siblings, 24 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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,
	Krzysztof Kozlowski

Hi all,

Here it goes v3. There was some major refactoring in this version due to
Lee's and Laurent's feedback. There are some splits (and some explicit
requests) resulting in new patches being added. The biggest change is the
effort in trying to minimize the usage of specific child device bits in
the top level device (mainly stuff related to the keymap). I think now
it's fairly self contained and the only thing that we really need to
handle in the top device are the unlock and reset events as those can be
supported through both the input and gpio devices (via gpio_keys). This
results in a bit of more runtime complexity but well, that's life...

Another change is Lee's suggestion of making use of templates (for
regmap and chip specific data) and fill things up at probe.

I also refactored a bit the event handling so it's more generic now.
There were lot's of changes so odds are that I might have forgotten some
feedback and so, my apologies in advance :).

I also dropped the tags in:

patch 16/22 ("gpio: adp5585: support gpi events") as it has some
significant changes (replacing .init_valid_masks() with .request() and
.free())

Thanks!
- Nuno Sá

---
Changes in v3:
- Patch 2:
 * New patch (only add devices given in DT).
- Patch 5:
 * Don't include adp5585-keys (still not present at this point).
- Patch 6:
 * Alphabetical order for compatibles.
- Patch 7:
 * New patch. Refactor regmap_config and fill variant differences at probe.
- Patch 8:
 * Rework according to changes introduced in patch 7;
 * Drop the regs struct in this patch. 
- Patch 9:
 * New patch. Add a per chip register structure. 
- Patch 10:
 * Moved the per variant gpio register into the gpio driver;
 * Moved ADP558[59]_GPIO_{BANK_BIT} into the gpio driver;
 * Moved ADP5589_GPIO_MAX and dropped the max_{col|row}.
- Patch 11:
 * Moved the per variant pwm register into the pwm driver (hence adding a chip_info struct.
- Patch 12:
 * Renamed the -keys suffix in the unlock/reset to events as that's the code we give in dt.
- Patch 13:
 * New patch (add event handling in a more generic way).
- Patch 14:
 * Support reset and unlock events in a separate patch;
 * Reworked how these events are validated.
- Patch 15:
 * Add support for input devices in it's own patch;
 * Add a bitmap for marking pins busy so there's no overlaps between
   the input and gpio devices. 
- Patch 16:
 * Drop .init_valid_mask() and use .free() and .request() for checking
   pin availability;
 * Drop max_gpios variables as that info is now available from the top
   device;
 * Adapt events handling to the new code.
- Patch 17:
 * Moved DT pin parsing into the input driver;
 * Validate reset/unlock events that are generated by the keymap;
 * Use error instead of ret;
 * Drop call to input_set_drvdata();
 * Adapt events handling to the new code.
- Patch 20:
 * Add a comment on the reset sleep time.

- Link to v2: https://lore.kernel.org/r/20250415-dev-adp5589-fw-v2-0-3a799c3ed812@analog.com
- Link to v1: https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com

---
Nuno Sá (22):
      dt-bindings: mfd: adp5585: ease on the required properties
      mfd: adp5585: only add devices given in FW
      mfd: adp5585: enable oscilator during probe
      pwm: adp5585: don't control OSC_EN in the pwm driver
      mfd: adp5585: make use of MFD_CELL_NAME()
      dt-bindings: mfd: adp5585: document adp5589 I/O expander
      mfd: adp5585: refactor how regmap defaults are handled
      mfd: adp5585: add support for adp5589
      mfd: adp5585: add a per chip reg struture
      gpio: adp5585: add support for the adp5589 expander
      pwm: adp5585: add support for adp5589
      dt-bindings: mfd: adp5585: add properties for input events
      mfd: adp5585: add support for event handling
      mfd: adp5585: support reset and unlock events
      mfd: adp5585: add support for input devices
      gpio: adp5585: support gpi events
      Input: adp5585: Add Analog Devices ADP5585/89 support
      Input: adp5589: remove the driver
      mfd: adp5585: support getting vdd regulator
      dt-bindings: mfd: adp5585: document reset gpio
      mfd: adp5585: add support for a reset pin
      pwm: adp5585: make sure to include mod_devicetable.h

 .../devicetree/bindings/mfd/adi,adp5585.yaml       |  240 ++++-
 .../devicetree/bindings/trivial-devices.yaml       |    2 -
 MAINTAINERS                                        |    1 +
 drivers/gpio/Kconfig                               |    1 +
 drivers/gpio/gpio-adp5585.c                        |  348 ++++++-
 drivers/input/keyboard/Kconfig                     |   21 +-
 drivers/input/keyboard/Makefile                    |    2 +-
 drivers/input/keyboard/adp5585-keys.c              |  356 +++++++
 drivers/input/keyboard/adp5589-keys.c              | 1066 --------------------
 drivers/mfd/adp5585.c                              |  794 ++++++++++++++-
 drivers/pwm/pwm-adp5585.c                          |   79 +-
 include/linux/mfd/adp5585.h                        |  148 ++-
 12 files changed, 1852 insertions(+), 1206 deletions(-)
---
base-commit: 407f60a151df3c44397e5afc0111eb9b026c38d3
change-id: 20250311-dev-adp5589-fw-e04cfd945286
--

Thanks!
- Nuno Sá



^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 01/22] dt-bindings: mfd: adp5585: ease on the required properties
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 02/22] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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] 63+ messages in thread

* [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 01/22] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-13 14:34   ` Lee Jones
  2025-05-12 12:38 ` [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
                   ` (21 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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 | 45 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a 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,12 +116,40 @@ static const struct regmap_config adp5585_regmap_configs[] = {
 	},
 };
 
+static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
+			    struct mfd_cell **devs)
+{
+	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
+
+	if (device_property_present(dev, "#pwm-cells"))
+		has_pwm = 1;
+
+	if (device_property_present(dev, "#gpio-cells"))
+		has_gpio = 1;
+
+	if (!has_pwm && !has_gpio)
+		return -ENODEV;
+
+	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
+			     GFP_KERNEL);
+	if (!*devs)
+		return -ENOMEM;
+
+	if (has_pwm)
+		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
+	if (has_gpio)
+		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
+
+	return rc;
+}
+
 static int adp5585_i2c_probe(struct i2c_client *i2c)
 {
 	const struct regmap_config *regmap_config;
 	struct adp5585_dev *adp5585;
+	struct mfd_cell *devs;
 	unsigned int id;
-	int ret;
+	int ret, n_devs;
 
 	adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585), GFP_KERNEL);
 	if (!adp5585)
@@ -138,9 +172,12 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 		return dev_err_probe(&i2c->dev, -ENODEV,
 				     "Invalid device ID 0x%02x\n", id);
 
+	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
+	if (n_devs < 0)
+		return n_devs;
+
 	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
-				   adp5585_devs, ARRAY_SIZE(adp5585_devs),
-				   NULL, 0, NULL);
+				   devs, n_devs, NULL, 0, NULL);
 	if (ret)
 		return dev_err_probe(&i2c->dev, ret,
 				     "Failed to add child devices\n");

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 01/22] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 02/22] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-13 14:26   ` Lee Jones
  2025-05-13 15:24   ` Laurent Pinchart
  2025-05-12 12:38 ` [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
                   ` (20 subsequent siblings)
  23 siblings, 2 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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).

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 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab3b3395 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	return rc;
 }
 
+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;
@@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 	if (n_devs < 0)
 		return n_devs;
 
+	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;
+
 	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
 				   devs, n_devs, NULL, 0, NULL);
 	if (ret)

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (2 preceding siblings ...)
  2025-05-12 12:38 ` [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-13 15:26   ` Laurent Pinchart
  2025-05-12 12:38 ` [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
                   ` (19 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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 Multi Function Device that can also be a gpio
controller and as it turns out, when OSC_EN is not set, we can't
reliably read the gpio value when it's configured as input. Hence,
OSC_EN will be set during probe by the parent device (and cleared on
unbind).

Moreover, we'll add support for the keymap matrix (input device) which
definitely needs OSC_EN to be set and so, we cannot afford that disabling
the PWM output also breaks the keymap events generation.

Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/pwm/pwm-adp5585.c | 5 -----
 1 file changed, 5 deletions(-)

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] 63+ messages in thread

* [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME()
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (3 preceding siblings ...)
  2025-05-12 12:38 ` [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-13 14:39   ` Lee Jones
  2025-05-12 12:38 ` [PATCH v3 06/22] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
                   ` (18 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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 d693b1ead05128e02f671ca465f9c72cab3b3395..19d4a0ab1bb4c261e82559630624059529765fbd 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] 63+ messages in thread

* [PATCH v3 06/22] dt-bindings: mfd: adp5585: document adp5589 I/O expander
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (4 preceding siblings ...)
  2025-05-12 12:38 ` [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-12 12:38 ` [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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] 63+ messages in thread

* [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (5 preceding siblings ...)
  2025-05-12 12:38 ` [PATCH v3 06/22] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
@ 2025-05-12 12:38 ` Nuno Sá via B4 Relay
  2025-05-13 15:00   ` Lee Jones
  2025-05-12 12:39 ` [PATCH v3 08/22] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
                   ` (16 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:38 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       | 94 +++++++++++++++++++++++++--------------------
 include/linux/mfd/adp5585.h | 11 ++++++
 2 files changed, 64 insertions(+), 41 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c19667af 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -81,41 +81,34 @@ 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,
+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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
+				      struct regmap_config *regmap_config)
+{
+	*regmap_config = adp5585_regmap_config_template;
+
+	switch (adp5585->info->regmap_type) {
+	case ADP5585_REGMAP_00:
+		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_00;
+		return 0;
+	case ADP5585_REGMAP_02:
+		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_02;
+		return 0;
+	case ADP5585_REGMAP_04:
+		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_04;
+		return 0;
+	default:
+		return -ENODEV;
+	}
+}
 
 static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 			    struct mfd_cell **devs)
@@ -153,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;
 	struct mfd_cell *devs;
 	unsigned int id;
@@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
+	if (!adp5585->info)
+		return -ENODEV;
+
+	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
+	if (ret)
+		return ret;
+
+	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
 	if (IS_ERR(adp5585->regmap))
 		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
 				     "Failed to initialize register map\n");
@@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
 
 static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
 
+static struct adp5585_info adp5585_info = {
+	.regmap_type = ADP5585_REGMAP_00,
+};
+
+static struct adp5585_info adp5585_02_info = {
+	.regmap_type = ADP5585_REGMAP_02,
+};
+
+static struct adp5585_info adp5585_04_info = {
+	.regmap_type = ADP5585_REGMAP_04,
+};
+
 static const struct of_device_id adp5585_of_match[] = {
 	{
 		.compatible = "adi,adp5585-00",
-		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+		.data = &adp5585_info,
 	}, {
 		.compatible = "adi,adp5585-01",
-		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+		.data = &adp5585_info,
 	}, {
 		.compatible = "adi,adp5585-02",
-		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
+		.data = &adp5585_02_info,
 	}, {
 		.compatible = "adi,adp5585-03",
-		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+		.data = &adp5585_info,
 	}, {
 		.compatible = "adi,adp5585-04",
-		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
+		.data = &adp5585_04_info,
 	},
 	{ /* sentinel */ }
 };
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea4f19ede6 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -119,8 +119,19 @@
 
 struct regmap;
 
+enum adp5585_regmap_type {
+	ADP5585_REGMAP_00,
+	ADP5585_REGMAP_02,
+	ADP5585_REGMAP_04,
+};
+
+struct adp5585_info {
+	enum adp5585_regmap_type regmap_type;
+};
+
 struct adp5585_dev {
 	struct regmap *regmap;
+	const struct adp5585_info *info;
 };
 
 #endif

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 08/22] mfd: adp5585: add support for adp5589
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (6 preceding siblings ...)
  2025-05-12 12:38 ` [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 09/22] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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       | 119 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/mfd/adp5585.h |  10 ++++
 2 files changed, 125 insertions(+), 4 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 874aed7d7cfe052587720d899096c995c19667af..d593d21920c960f397a79f1b3f5c7118fedea73a 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,54 @@ 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,
+};
+
+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 const struct regmap_config adp5585_regmap_config_template = {
 	.reg_bits = 8,
 	.val_bits = 8,
@@ -93,7 +155,10 @@ static const struct regmap_config adp5585_regmap_config_template = {
 static int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
 				      struct regmap_config *regmap_config)
 {
-	*regmap_config = adp5585_regmap_config_template;
+	if (adp5585->info->id == ADP5585_MAN_ID_VALUE)
+		*regmap_config = adp5585_regmap_config_template;
+	else
+		*regmap_config = adp5589_regmap_config_template;
 
 	switch (adp5585->info->regmap_type) {
 	case ADP5585_REGMAP_00:
@@ -105,6 +170,15 @@ static int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
 	case ADP5585_REGMAP_04:
 		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_04;
 		return 0;
+	case ADP5589_REGMAP_00:
+		regmap_config->reg_defaults_raw = adp5589_regmap_defaults_00;
+		return 0;
+	case ADP5589_REGMAP_01:
+		regmap_config->reg_defaults_raw = adp5589_regmap_defaults_01;
+		return 0;
+	case ADP5589_REGMAP_02:
+		regmap_config->reg_defaults_raw = adp5589_regmap_defaults_02;
+		return 0;
 	default:
 		return -ENODEV;
 	}
@@ -114,6 +188,7 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 			    struct mfd_cell **devs)
 {
 	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
+	const struct mfd_cell *cells;
 
 	if (device_property_present(dev, "#pwm-cells"))
 		has_pwm = 1;
@@ -129,10 +204,15 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	if (!*devs)
 		return -ENOMEM;
 
+	if (adp5585->info->id == ADP5585_MAN_ID_VALUE)
+		cells = adp5585_devs;
+	else
+		cells = adp5589_devs;
+
 	if (has_pwm)
-		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
+		(*devs)[rc++] = cells[ADP5585_DEV_PWM];
 	if (has_gpio)
-		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
+		(*devs)[rc++] = cells[ADP5585_DEV_GPIO];
 
 	return rc;
 }
@@ -176,7 +256,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 		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->info->id)
 		return dev_err_probe(&i2c->dev, -ENODEV,
 				     "Invalid device ID 0x%02x\n", id);
 
@@ -225,14 +306,32 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
 
 static struct adp5585_info adp5585_info = {
 	.regmap_type = ADP5585_REGMAP_00,
+	.id = ADP5585_MAN_ID_VALUE,
 };
 
 static struct adp5585_info adp5585_02_info = {
 	.regmap_type = ADP5585_REGMAP_02,
+	.id = ADP5585_MAN_ID_VALUE,
 };
 
 static struct adp5585_info adp5585_04_info = {
 	.regmap_type = ADP5585_REGMAP_04,
+	.id = ADP5585_MAN_ID_VALUE,
+};
+
+static struct adp5585_info adp5589_info = {
+	.regmap_type = ADP5589_REGMAP_00,
+	.id = ADP5589_MAN_ID_VALUE,
+};
+
+static struct adp5585_info adp5589_01_info = {
+	.regmap_type = ADP5589_REGMAP_01,
+	.id = ADP5589_MAN_ID_VALUE,
+};
+
+static struct adp5585_info adp5589_02_info = {
+	.regmap_type = ADP5589_REGMAP_02,
+	.id = ADP5589_MAN_ID_VALUE,
 };
 
 static const struct of_device_id adp5585_of_match[] = {
@@ -251,6 +350,18 @@ static const struct of_device_id adp5585_of_match[] = {
 	}, {
 		.compatible = "adi,adp5585-04",
 		.data = &adp5585_04_info,
+	}, {
+		.compatible = "adi,adp5589-00",
+		.data = &adp5589_info,
+	}, {
+		.compatible = "adi,adp5589-01",
+		.data = &adp5589_01_info,
+	}, {
+		.compatible = "adi,adp5589-02",
+		.data = &adp5589_02_info,
+	}, {
+		.compatible = "adi,adp5589",
+		.data = &adp5589_info,
 	},
 	{ /* sentinel */ }
 };
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 4b48614970a811a8a95116faa20e58ea4f19ede6..5e19e38d4eac563275b01c3ec613ea62eba9d6c6 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -117,16 +117,26 @@
 #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_regmap_type {
 	ADP5585_REGMAP_00,
 	ADP5585_REGMAP_02,
 	ADP5585_REGMAP_04,
+	ADP5589_REGMAP_00,
+	ADP5589_REGMAP_01,
+	ADP5589_REGMAP_02,
 };
 
 struct adp5585_info {
 	enum adp5585_regmap_type regmap_type;
+	unsigned int id;
 };
 
 struct adp5585_dev {

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 09/22] mfd: adp5585: add a per chip reg struture
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (7 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 08/22] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 10/22] gpio: adp5585: add support for the adp5589 expander Nuno Sá via B4 Relay
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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       | 39 ++++++++++++++++++++++++++++++---------
 include/linux/mfd/adp5585.h |  6 ++++++
 2 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index d593d21920c960f397a79f1b3f5c7118fedea73a..8be7a76842f639cbe90ad0eb956a7a3eef43fa3d 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -152,13 +152,38 @@ static const struct regmap_config adp5585_regmap_config_template = {
 	.num_reg_defaults_raw = ADP5585_MAX_REG + 1,
 };
 
-static int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
-				      struct regmap_config *regmap_config)
+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_chip_configs(struct adp5585_dev *adp5585,
+				     struct i2c_client *i2c,
+				     struct regmap_config *regmap_config)
 {
-	if (adp5585->info->id == ADP5585_MAN_ID_VALUE)
+	struct adp5585_info *info;
+
+	info = (struct adp5585_info *)i2c_get_match_data(i2c);
+	if (!info)
+		return -ENODEV;
+
+	switch (info->id) {
+	case ADP5585_MAN_ID_VALUE:
 		*regmap_config = adp5585_regmap_config_template;
-	else
+		info->regs = &adp5585_regs;
+		break;
+	case ADP5589_MAN_ID_VALUE:
 		*regmap_config = adp5589_regmap_config_template;
+		info->regs = &adp5589_regs;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	adp5585->info = info;
 
 	switch (adp5585->info->regmap_type) {
 	case ADP5585_REGMAP_00:
@@ -238,11 +263,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 
 	i2c_set_clientdata(i2c, adp5585);
 
-	adp5585->info = i2c_get_match_data(i2c);
-	if (!adp5585->info)
-		return -ENODEV;
-
-	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
+	ret = adp5585_fill_chip_configs(adp5585, i2c, &regmap_config);
 	if (ret)
 		return ret;
 
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 5e19e38d4eac563275b01c3ec613ea62eba9d6c6..c8fa9684ecd3e869ab1fed7f257a340bfa4602f9 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
 
@@ -134,7 +135,12 @@ enum adp5585_regmap_type {
 	ADP5589_REGMAP_02,
 };
 
+struct adp5585_regs {
+	unsigned int ext_cfg;
+};
+
 struct adp5585_info {
+	const struct adp5585_regs *regs;
 	enum adp5585_regmap_type regmap_type;
 	unsigned int id;
 };

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 10/22] gpio: adp5585: add support for the adp5589 expander
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (8 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 09/22] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 11/22] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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 c8fa9684ecd3e869ab1fed7f257a340bfa4602f9..7865943e71ed17d63c1b9f30001bb93ab3aa6684 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_regmap_type {

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 11/22] pwm: adp5585: add support for adp5589
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (9 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 10/22] gpio: adp5585: add support for the adp5589 expander Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 12/22] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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..f26054c19c2e154d05780af09aee1b2431eba2eb 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->info->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 7865943e71ed17d63c1b9f30001bb93ab3aa6684..9a925a91c772722db559c9ec8ae334b2159ede79 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] 63+ messages in thread

* [PATCH v3 12/22] dt-bindings: mfd: adp5585: add properties for input events
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (10 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 11/22] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 13/22] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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] 63+ messages in thread

* [PATCH v3 13/22] mfd: adp5585: add support for event handling
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (11 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 12/22] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-13 15:59   ` Lee Jones
  2025-05-12 12:39 ` [PATCH v3 14/22] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
                   ` (10 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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       | 180 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/mfd/adp5585.h |  48 ++++++++++++
 2 files changed, 223 insertions(+), 5 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 8be7a76842f639cbe90ad0eb956a7a3eef43fa3d..5851ad30e7323bbb891878167d0786bc60ef5d90 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -154,10 +154,16 @@ static const struct regmap_config adp5585_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_chip_configs(struct adp5585_dev *adp5585,
@@ -214,6 +220,8 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 {
 	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
 	const struct mfd_cell *cells;
+	unsigned int prop_val;
+	int ret;
 
 	if (device_property_present(dev, "#pwm-cells"))
 		has_pwm = 1;
@@ -224,6 +232,25 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	if (!has_pwm && !has_gpio)
 		return -ENODEV;
 
+	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
+	if (!ret) {
+		switch (prop_val) {
+		case 10:
+			fallthrough;
+		case 20:
+			fallthrough;
+		case 30:
+			fallthrough;
+		case 40:
+			adp5585->ev_poll_time = prop_val / 10 - 1;
+			break;
+		default:
+			return dev_err_probe(dev, -EINVAL,
+					     "Invalid value(%u) for poll-interval\n",
+					     prop_val);
+		}
+	}
+
 	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
 			     GFP_KERNEL);
 	if (!*devs)
@@ -249,6 +276,135 @@ 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)
+{
+	struct adp5585_ev_handler *h;
+	unsigned int i;
+
+	guard(mutex)(&adp5585->ev_lock);
+
+	if (list_empty(&adp5585->ev_handlers)) {
+		dev_warn_ratelimited(adp5585->dev, "No event handlers registered\n");
+		return;
+	}
+
+	for (i = 0; i < ev_cnt; i++) {
+		unsigned int key, key_val, key_press;
+		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);
+
+		list_for_each_entry(h, &adp5585->ev_handlers, entry) {
+			ret = h->handler(h->dev, key_val, key_press);
+			if (!ret)
+				/* handled! */
+				break;
+		}
+	}
+}
+
+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->info->regs;
+	unsigned int reg_val, i;
+	int ret;
+
+	for (i = 0; i < ADP5585_EV_MAX; i++) {
+		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
+			   adp5585->ev_poll_time);
+	if (ret)
+		return ret;
+
+	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 void adp5585_irq_disable(void *data)
+{
+	struct adp5585_dev *adp5585 = data;
+
+	regmap_write(adp5585->regmap, adp5585->info->regs->int_en, 0);
+}
+
+static int adp5585_irq_enable(struct i2c_client *i2c,
+			      struct adp5585_dev *adp5585)
+{
+	const struct adp5585_regs *regs = adp5585->info->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... */
+	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;
@@ -282,16 +438,19 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 		return dev_err_probe(&i2c->dev, -ENODEV,
 				     "Invalid device ID 0x%02x\n", id);
 
+	adp5585->dev = &i2c->dev;
+	adp5585->irq = i2c->irq;
+	INIT_LIST_HEAD(&adp5585->ev_handlers);
+
 	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
 	if (n_devs < 0)
 		return n_devs;
 
-	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
-			      ADP5585_OSC_EN);
+	ret = adp5585_setup(adp5585);
 	if (ret)
 		return ret;
 
-	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
+	ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
 	if (ret)
 		return ret;
 
@@ -301,13 +460,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 		return dev_err_probe(&i2c->dev, ret,
 				     "Failed to add child devices\n");
 
-	return 0;
+	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;
@@ -316,11 +478,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 9a925a91c772722db559c9ec8ae334b2159ede79..218c56bed2e0304de8b81c7090386fb4e1b6c281 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -10,13 +10,23 @@
 #define __MFD_ADP5585_H_
 
 #include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mutex.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 +42,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 +115,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 +134,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
 
@@ -138,8 +153,18 @@ enum adp5585_regmap_type {
 	ADP5589_REGMAP_02,
 };
 
+struct adp5585_ev_handler {
+	struct list_head entry;
+	struct device *dev;
+	int (*handler)(struct device *dev, unsigned int key_val,
+		       unsigned int key_press);
+};
+
 struct adp5585_regs {
+	unsigned int gen_cfg;
 	unsigned int ext_cfg;
+	unsigned int int_en;
+	unsigned int poll_ptime_cfg;
 };
 
 struct adp5585_info {
@@ -150,7 +175,30 @@ struct adp5585_info {
 
 struct adp5585_dev {
 	struct regmap *regmap;
+	struct device *dev;
 	const struct adp5585_info *info;
+	/* Used to synchronize the availability of the event handlers */
+	struct mutex ev_lock;
+	struct list_head ev_handlers;
+	int irq;
+	unsigned int ev_poll_time;
 };
 
+static inline void adp5585_ev_handler_remove(void *data)
+{
+	struct adp5585_ev_handler *handler = data;
+	struct adp5585_dev *adp5585 = dev_get_drvdata(handler->dev->parent);
+
+	guard(mutex)(&adp5585->ev_lock);
+	list_del(&handler->entry);
+}
+
+static inline int devm_adp5585_ev_handler_add(struct adp5585_dev *adp5585,
+					      struct adp5585_ev_handler *handler)
+{
+	guard(mutex)(&adp5585->ev_lock);
+	list_add_tail(&handler->entry, &adp5585->ev_handlers);
+	return devm_add_action_or_reset(handler->dev, adp5585_ev_handler_remove,
+					handler);
+}
 #endif

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 14/22] mfd: adp5585: support reset and unlock events
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (12 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 13/22] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-13 16:22   ` Lee Jones
  2025-05-12 12:39 ` [PATCH v3 15/22] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
                   ` (9 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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       | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/adp5585.h |  41 +++++++
 2 files changed, 320 insertions(+)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 5851ad30e7323bbb891878167d0786bc60ef5d90..b1227a390fe2f932ba8060b0d722f53f45ec3b4b 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -157,6 +157,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 = {
@@ -164,8 +167,52 @@ 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, bool has_pin5)
+{
+	if (has_pin5) {
+		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 GPI5 */
+		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, bool has_pin5)
+{
+	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_chip_configs(struct adp5585_dev *adp5585,
 				     struct i2c_client *i2c,
 				     struct regmap_config *regmap_config)
@@ -180,10 +227,13 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
 	case ADP5585_MAN_ID_VALUE:
 		*regmap_config = adp5585_regmap_config_template;
 		info->regs = &adp5585_regs;
+		info->validate_event = adp5585_validate_event;
 		break;
 	case ADP5589_MAN_ID_VALUE:
 		*regmap_config = adp5589_regmap_config_template;
 		info->regs = &adp5589_regs;
+		info->validate_event = adp5589_validate_event;
+		info->has_unlock = true;
 		break;
 	default:
 		return -ENODEV;
@@ -215,11 +265,175 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
 	}
 }
 
+static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585,
+				  const char *prop, u32 *events, u32 *n_events,
+				  u32 max_keys, bool reset_key, bool has_pin5)
+{
+	const struct adp5585_info *info = adp5585->info;
+	struct device *dev = adp5585->dev;
+	unsigned int ev;
+	int ret;
+
+	ret = device_property_count_u32(dev, prop);
+	if (ret < 0)
+		return 0;
+
+	*n_events = ret;
+
+	if (!info->has_unlock && !reset_key)
+		return dev_err_probe(dev, -EOPNOTSUPP,
+				     "Unlock keys not supported\n");
+
+	if (*n_events > max_keys)
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid number of keys(%u > %u) for %s\n",
+				     *n_events, max_keys, prop);
+
+	ret = device_property_read_u32_array(dev, prop, events, *n_events);
+	if (ret)
+		return ret;
+
+	for (ev = 0; ev < *n_events; ev++) {
+		/* wildcard key */
+		if (!reset_key && events[ev] == 127)
+			continue;
+
+		ret = adp5585->info->validate_event(adp5585, events[ev], has_pin5);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585, bool has_pin5)
+{
+	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,
+				     has_pin5);
+	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, bool has_pin5)
+{
+	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,
+				     has_pin5);
+	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,
+				     has_pin5);
+	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 int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 			    struct mfd_cell **devs)
 {
 	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
 	const struct mfd_cell *cells;
+	bool has_pin5 = false;
 	unsigned int prop_val;
 	int ret;
 
@@ -232,6 +446,17 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	if (!has_pwm && !has_gpio)
 		return -ENODEV;
 
+	if (!device_property_present(dev, "gpio-reserved-ranges"))
+		has_pin5 = true;
+
+	ret = adp5585_unlock_ev_parse(adp5585, has_pin5);
+	if (ret)
+		return ret;
+
+	ret = adp5585_reset_ev_parse(adp5585, has_pin5);
+	if (ret)
+		return ret;
+
 	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
 	if (!ret) {
 		switch (prop_val) {
@@ -344,6 +569,60 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
 	unsigned int reg_val, i;
 	int ret;
 
+	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 (adp5585->nkeys_reset1)
+			reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
+		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;
+	}
+
 	for (i = 0; i < ADP5585_EV_MAX; i++) {
 		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
 		if (ret)
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 218c56bed2e0304de8b81c7090386fb4e1b6c281..034b7c18af83b1e801860ed4fca1755ff59faed1 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -71,6 +71,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
@@ -121,6 +122,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
@@ -131,6 +139,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
@@ -141,8 +163,13 @@
 #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;
+struct adp5585_dev;
 
 enum adp5585_regmap_type {
 	ADP5585_REGMAP_00,
@@ -165,12 +192,18 @@ 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_info {
 	const struct adp5585_regs *regs;
+	int (*validate_event)(const struct adp5585_dev *adp5585,
+			      unsigned int ev, bool has_pin5);
 	enum adp5585_regmap_type regmap_type;
 	unsigned int id;
+	bool has_unlock;
 };
 
 struct adp5585_dev {
@@ -182,6 +215,14 @@ struct adp5585_dev {
 	struct list_head ev_handlers;
 	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;
 };
 
 static inline void adp5585_ev_handler_remove(void *data)

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 15/22] mfd: adp5585: add support for input devices
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (13 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 14/22] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 16/22] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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       | 38 +++++++++++++++++++++++++++++++++-----
 include/linux/mfd/adp5585.h | 11 +++++++++++
 2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index b1227a390fe2f932ba8060b0d722f53f45ec3b4b..5a4a24593a83271e8a8df40022b73dfa9c15a114 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -21,17 +21,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[] = {
@@ -160,6 +163,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 = {
@@ -170,6 +174,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,
@@ -228,12 +233,16 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
 		*regmap_config = adp5585_regmap_config_template;
 		info->regs = &adp5585_regs;
 		info->validate_event = adp5585_validate_event;
+		info->n_pins = ADP5585_PIN_MAX;
+		info->reset2_out = ADP5585_RESET2_OUT;
 		break;
 	case ADP5589_MAN_ID_VALUE:
 		*regmap_config = adp5589_regmap_config_template;
 		info->regs = &adp5589_regs;
 		info->validate_event = adp5589_validate_event;
 		info->has_unlock = true;
+		info->n_pins = ADP5589_PIN_MAX;
+		info->reset2_out = ADP5589_RESET2_OUT;
 		break;
 	default:
 		return -ENODEV;
@@ -431,7 +440,7 @@ static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585, bool has_pin5)
 static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 			    struct mfd_cell **devs)
 {
-	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
+	unsigned int has_pwm = 0, has_gpio = 0, has_input = 0, rc = 0;
 	const struct mfd_cell *cells;
 	bool has_pin5 = false;
 	unsigned int prop_val;
@@ -443,11 +452,16 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	if (device_property_present(dev, "#gpio-cells"))
 		has_gpio = 1;
 
-	if (!has_pwm && !has_gpio)
+	if (device_property_present(dev, "adi,keypad-pins"))
+		has_input = 1;
+
+	if (!has_pwm && !has_gpio && !has_input)
 		return -ENODEV;
 
 	if (!device_property_present(dev, "gpio-reserved-ranges"))
 		has_pin5 = true;
+	else
+		__set_bit(5, adp5585->pin_usage);
 
 	ret = adp5585_unlock_ev_parse(adp5585, has_pin5);
 	if (ret)
@@ -476,8 +490,8 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 		}
 	}
 
-	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
-			     GFP_KERNEL);
+	*devs = devm_kcalloc(dev, has_pwm + has_gpio + has_input,
+			     sizeof(struct mfd_cell), GFP_KERNEL);
 	if (!*devs)
 		return -ENOMEM;
 
@@ -486,10 +500,14 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
 	else
 		cells = adp5589_devs;
 
-	if (has_pwm)
+	if (has_pwm) {
+		__set_bit(ADP5585_PWM_OUT, adp5585->pin_usage);
 		(*devs)[rc++] = cells[ADP5585_DEV_PWM];
+	}
 	if (has_gpio)
 		(*devs)[rc++] = cells[ADP5585_DEV_GPIO];
+	if (has_input)
+		(*devs)[rc++] = cells[ADP5585_DEV_INPUT];
 
 	return rc;
 }
@@ -594,6 +612,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++) {
@@ -601,6 +622,8 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
 				   adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
 		if (ret)
 			return ret;
+
+		__set_bit(adp5585->info->reset2_out, adp5585->pin_usage);
 	}
 
 	if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
@@ -721,6 +744,11 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 	adp5585->irq = i2c->irq;
 	INIT_LIST_HEAD(&adp5585->ev_handlers);
 
+	adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->info->n_pins,
+						GFP_KERNEL);
+	if (!adp5585->pin_usage)
+		return -ENOMEM;
+
 	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
 	if (n_devs < 0)
 		return n_devs;
diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
index 034b7c18af83b1e801860ed4fca1755ff59faed1..1cec7f72781999ae81e179c7eefd4539de59bc97 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -129,12 +129,17 @@
 #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
 #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
@@ -157,6 +162,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
@@ -167,6 +173,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;
 struct adp5585_dev;
@@ -195,6 +202,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_info {
@@ -203,6 +211,8 @@ struct adp5585_info {
 			      unsigned int ev, bool has_pin5);
 	enum adp5585_regmap_type regmap_type;
 	unsigned int id;
+	unsigned int n_pins;
+	unsigned int reset2_out;
 	bool has_unlock;
 };
 
@@ -210,6 +220,7 @@ struct adp5585_dev {
 	struct regmap *regmap;
 	struct device *dev;
 	const struct adp5585_info *info;
+	unsigned long *pin_usage;
 	/* Used to synchronize the availability of the event handlers */
 	struct mutex ev_lock;
 	struct list_head ev_handlers;

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 16/22] gpio: adp5585: support gpi events
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (14 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 15/22] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
                   ` (7 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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 | 205 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 202 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..cd0ddddae7db1f4634680b412b8fcbfa295e87eb 100644
--- a/drivers/gpio/gpio-adp5585.c
+++ b/drivers/gpio/gpio-adp5585.c
@@ -7,10 +7,13 @@
  * Copyright 2025 Analog Devices, Inc.
  */
 
+#include <linux/bitmap.h>
+#include <linux/bitops.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/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/types.h>
@@ -36,20 +39,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 adp5585_ev_handler ev_handler;
 	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 +236,163 @@ 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->info->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 device *dev, unsigned int key,
+				  unsigned int key_press)
+{
+	struct adp5585_gpio_dev *adp5585_gpio = dev_get_drvdata(dev);
+	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 -EINVAL;
+
+	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 0;
+
+	irqd = irq_get_irq_data(irq);
+	if (!irqd) {
+		dev_err(dev, "Could not get irq(%u) data\n", irq);
+		return 0;
+	}
+
+	dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%u, 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 0;
+}
+
+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 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 +416,41 @@ 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->info->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->ev_handler.dev = dev;
+		adp5585_gpio->ev_handler.handler = adp5585_gpio_key_event;
+		platform_set_drvdata(pdev, adp5585_gpio);
+
+		ret = devm_adp5585_ev_handler_add(adp5585,
+						  &adp5585_gpio->ev_handler);
+		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 +468,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 +484,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[] = {

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (15 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 16/22] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-19 22:29   ` Dmitry Torokhov
  2025-05-12 12:39 ` [PATCH v3 18/22] Input: adp5589: remove the driver Nuno Sá via B4 Relay
                   ` (6 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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 | 356 ++++++++++++++++++++++++++++++++++
 4 files changed, 369 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..54e8fefcfab8942507245aa27931ccaf8d195b8e
--- /dev/null
+++ b/drivers/input/keyboard/adp5585-keys.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADP5585 Keys driver
+ *
+ * Copyright (C) 2025 Analog Devices, Inc.
+ */
+
+#include <linux/bitmap.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/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 adp5585_ev_handler ev_handler;
+	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->info->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->info->n_pins)
+		return dev_err_probe(dev, -EINVAL,
+				     "Too many keypad pins (%d) defined (max=%d)\n",
+				     n_pins, adp5585->info->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;
+
+	for (pin = 0; pin < n_pins; pin++) {
+		if (keypad_pins[pin] >= adp5585->info->n_pins) {
+			error = dev_err_probe(dev, -EINVAL,
+					      "Invalid keypad pin(%u) defined\n",
+					      keypad_pins[pin]);
+			goto out_free_map;
+		}
+
+		if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) {
+			error = dev_err_probe(dev, -EBUSY,
+					      "Keypad pin(%u) already used\n",
+					      keypad_pins[pin]);
+			goto out_free_map;
+		}
+
+		__set_bit(keypad_pins[pin], &kpad->keypad);
+	}
+
+	error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad);
+	if (error)
+		return error;
+
+	/*
+	 * 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);
+
+	return adp5585_keys_check_special_events(adp5585, kpad);
+
+out_free_map:
+	adp5585_keys_pins_free(kpad);
+	return error;
+}
+
+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->info->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 device *dev, unsigned int key,
+				  unsigned int key_press)
+{
+	struct adp5585_kpad *kpad = dev_get_drvdata(dev);
+	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 -EINVAL;
+
+	/*
+	 * 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 0;
+
+	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(%d) 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 0;
+}
+
+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;
+
+	platform_set_drvdata(pdev, kpad);
+	kpad->ev_handler.dev = dev;
+	kpad->ev_handler.handler = adp5585_keys_ev_handle;
+
+	error = devm_adp5585_ev_handler_add(adp5585, &kpad->ev_handler);
+	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] 63+ messages in thread

* [PATCH v3 18/22] Input: adp5589: remove the driver
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (16 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 19/22] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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] 63+ messages in thread

* [PATCH v3 19/22] mfd: adp5585: support getting vdd regulator
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (17 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 18/22] Input: adp5589: remove the driver Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 20/22] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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 5a4a24593a83271e8a8df40022b73dfa9c15a114..88401668f30e06ac201175470eeaf6216f3121d9 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -16,6 +16,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/types.h>
 
 enum {
@@ -725,6 +726,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, &regmap_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] 63+ messages in thread

* [PATCH v3 20/22] dt-bindings: mfd: adp5585: document reset gpio
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (18 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 19/22] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-12 12:39 ` [PATCH v3 21/22] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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] 63+ messages in thread

* [PATCH v3 21/22] mfd: adp5585: add support for a reset pin
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (19 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 20/22] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-13 16:26   ` Lee Jones
  2025-05-12 12:39 ` [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
                   ` (2 subsequent siblings)
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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 88401668f30e06ac201175470eeaf6216f3121d9..0fbe1f7f2582408b2e1b99f629182ceebce73fd7 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -11,6 +11,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>
@@ -712,6 +713,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
 {
 	struct regmap_config regmap_config;
 	struct adp5585_dev *adp5585;
+	struct gpio_desc *gpio;
 	struct mfd_cell *devs;
 	unsigned int id;
 	int ret, n_devs;
@@ -730,6 +732,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, &regmap_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] 63+ messages in thread

* [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (20 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 21/22] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
@ 2025-05-12 12:39 ` Nuno Sá via B4 Relay
  2025-05-19 16:11   ` Uwe Kleine-König
  2025-05-14  8:25 ` [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Lee Jones
  2025-07-02 13:34 ` Lee Jones
  23 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-05-12 12:39 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>

Explicitly include mod_devicetable.h for struct platform_device_id.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/pwm/pwm-adp5585.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
index f26054c19c2e154d05780af09aee1b2431eba2eb..93d0294d048abfe1a009161025e658b58b669cd9 100644
--- a/drivers/pwm/pwm-adp5585.c
+++ b/drivers/pwm/pwm-adp5585.c
@@ -20,6 +20,7 @@
 #include <linux/mfd/adp5585.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
+#include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>

-- 
2.49.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-12 12:38 ` [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-05-13 14:26   ` Lee Jones
  2025-05-13 14:52     ` Nuno Sá
  2025-05-13 15:24   ` Laurent Pinchart
  1 sibling, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-13 14:26 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 Mon, 12 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).
> 
> 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 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab3b3395 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  	return rc;
>  }
>  
> +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;
> @@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  	if (n_devs < 0)
>  		return n_devs;
>  
> +	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> +			      ADP5585_OSC_EN);

Nit: Consider unwrapping to 100-chars to avoid these simple line breaks.

Other than that, looks okay.

> +	if (ret)
> +		return ret;
> +
> +	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> +	if (ret)
> +		return ret;
> +
>  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
>  				   devs, n_devs, NULL, 0, NULL);
>  	if (ret)
> 
> -- 
> 2.49.0
> 
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-12 12:38 ` [PATCH v3 02/22] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
@ 2025-05-13 14:34   ` Lee Jones
  2025-05-13 15:02     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-13 14:34 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 Mon, 12 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 | 45 +++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 41 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a 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,12 +116,40 @@ static const struct regmap_config adp5585_regmap_configs[] = {
>  	},
>  };
>  
> +static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> +			    struct mfd_cell **devs)
> +{
> +	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> +
> +	if (device_property_present(dev, "#pwm-cells"))
> +		has_pwm = 1;

This is a little sloppy.  Instead of using throwaway local variables, do
what you're going to do in the if statement.

> +	if (device_property_present(dev, "#gpio-cells"))
> +		has_gpio = 1;
> +
> +	if (!has_pwm && !has_gpio)
> +		return -ENODEV;

Are we really dictating which child devices to register based on random
DT properties?  Why not register them anyway and have them fail if the
information they need is not available?  Missing / incorrect properties
usually get a -EINVAL.

> +	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
> +			     GFP_KERNEL);
> +	if (!*devs)
> +		return -ENOMEM;
> +
> +	if (has_pwm)
> +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
> +	if (has_gpio)
> +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];

Passing around pointers to pointers for allocation (and later, pointer
to functions) is not the way we wish to operate.  See how all of the
other MFD drivers handle selective sub-drivers.

> +	return rc;
> +}
> +
>  static int adp5585_i2c_probe(struct i2c_client *i2c)
>  {
>  	const struct regmap_config *regmap_config;
>  	struct adp5585_dev *adp5585;
> +	struct mfd_cell *devs;
>  	unsigned int id;
> -	int ret;
> +	int ret, n_devs;
>  
>  	adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585), GFP_KERNEL);
>  	if (!adp5585)
> @@ -138,9 +172,12 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  		return dev_err_probe(&i2c->dev, -ENODEV,
>  				     "Invalid device ID 0x%02x\n", id);
>  
> +	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> +	if (n_devs < 0)
> +		return n_devs;
> +
>  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> -				   adp5585_devs, ARRAY_SIZE(adp5585_devs),
> -				   NULL, 0, NULL);
> +				   devs, n_devs, NULL, 0, NULL);
>  	if (ret)
>  		return dev_err_probe(&i2c->dev, ret,
>  				     "Failed to add child devices\n");
> 
> -- 
> 2.49.0
> 
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME()
  2025-05-12 12:38 ` [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-05-13 14:39   ` Lee Jones
  2025-05-13 14:50     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-13 14:39 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 Mon, 12 May 2025, Nuno Sá via B4 Relay wrote:

> 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 d693b1ead05128e02f671ca465f9c72cab3b3395..19d4a0ab1bb4c261e82559630624059529765fbd 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.

If you're going to sneak in irrelevant changes, at least mention it in
passing in the change log.

>   */
>  
>  #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] 63+ messages in thread

* Re: [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME()
  2025-05-13 14:39   ` Lee Jones
@ 2025-05-13 14:50     ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 14:50 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 Tue, 2025-05-13 at 15:39 +0100, Lee Jones wrote:
> On Mon, 12 May 2025, Nuno Sá via B4 Relay wrote:
> 
> > 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
> > d693b1ead05128e02f671ca465f9c72cab3b3395..19d4a0ab1bb4c261e82559630624059529
> > 765fbd 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.
> 
> If you're going to sneak in irrelevant changes, at least mention it in
> passing in the change log.

I actually thought this was needy and is present since v1... Can mention it in
the commit message in the next version.

> 
> >   */
> >  
> >  #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	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-13 14:26   ` Lee Jones
@ 2025-05-13 14:52     ` Nuno Sá
  2025-05-13 16:07       ` Lee Jones
  0 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 14:52 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 Tue, 2025-05-13 at 15:26 +0100, Lee Jones wrote:
> On Mon, 12 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).
> > 
> > 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
> > 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab
> > 3b3395 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  	return rc;
> >  }
> >  
> > +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;
> > @@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  	if (n_devs < 0)
> >  		return n_devs;
> >  
> > +	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > +			      ADP5585_OSC_EN);
> 
> Nit: Consider unwrapping to 100-chars to avoid these simple line breaks.
> 
> Other than that, looks okay.

This topic is always hard as some other maintainers perfect the rule "keep the
80 char and only go 100 if readability is hurt). Personally, I do prefer 100 so
happy to do it here.

> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable,
> > adp5585);
> > +	if (ret)
> > +		return ret;
> > +
> >  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> >  				   devs, n_devs, NULL, 0, NULL);
> >  	if (ret)
> > 
> > -- 
> > 2.49.0
> > 
> > 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-12 12:38 ` [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
@ 2025-05-13 15:00   ` Lee Jones
  2025-05-13 15:02     ` Lee Jones
                       ` (2 more replies)
  0 siblings, 3 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 15:00 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 Mon, 12 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
> (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       | 94 +++++++++++++++++++++++++--------------------
>  include/linux/mfd/adp5585.h | 11 ++++++
>  2 files changed, 64 insertions(+), 41 deletions(-)
> 
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c19667af 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -81,41 +81,34 @@ 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,
> +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> +				      struct regmap_config *regmap_config)

I like the general idea.  This is much more scaleable than before.

> +{
> +	*regmap_config = adp5585_regmap_config_template;
> +
> +	switch (adp5585->info->regmap_type) {
> +	case ADP5585_REGMAP_00:
> +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_00;
> +		return 0;
> +	case ADP5585_REGMAP_02:
> +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_02;
> +		return 0;
> +	case ADP5585_REGMAP_04:
> +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_04;

You could make this read a tiny bit nicer (as you do with the adp5585->info
in a later patch) and make reg_defaults_raw a local variable.

> +		return 0;
> +	default:
> +		return -ENODEV;
> +	}
> +}
>  
>  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  			    struct mfd_cell **devs)
> @@ -153,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;
>  	struct mfd_cell *devs;
>  	unsigned int id;
> @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> +	if (!adp5585->info)
> +		return -ENODEV;
> +
> +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> +	if (ret)
> +		return ret;
> +
> +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
>  	if (IS_ERR(adp5585->regmap))
>  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
>  				     "Failed to initialize register map\n");
> @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
>  
>  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
>  
> +static struct adp5585_info adp5585_info = {
> +	.regmap_type = ADP5585_REGMAP_00,

Instead of providing this enum, then later another one (id) which is a
subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
through the DT .data attribute then match on those?  It will add a
couple of lines to the switch(info->id) statement, but will save on a
boat load of static structs and other complexity.

For instance:

switch (info->id) {
	case ADP5585_MAN_ID_VALUE:

Would simply become:

switch (info->id) {
	case ADP5585_REGMAP_00:
	case ADP5585_REGMAP_02:
	case ADP5585_REGMAP_04:

And that's it.

> +};
> +
> +static struct adp5585_info adp5585_02_info = {
> +	.regmap_type = ADP5585_REGMAP_02,
> +};
> +
> +static struct adp5585_info adp5585_04_info = {
> +	.regmap_type = ADP5585_REGMAP_04,
> +};
> +
>  static const struct of_device_id adp5585_of_match[] = {
>  	{
>  		.compatible = "adi,adp5585-00",
> -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> +		.data = &adp5585_info,

		.data = ADP5585_REGMAP_00,

>  	}, {
>  		.compatible = "adi,adp5585-01",
> -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> +		.data = &adp5585_info,
>  	}, {
>  		.compatible = "adi,adp5585-02",
> -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> +		.data = &adp5585_02_info,
>  	}, {
>  		.compatible = "adi,adp5585-03",
> -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> +		.data = &adp5585_info,
>  	}, {
>  		.compatible = "adi,adp5585-04",
> -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> +		.data = &adp5585_04_info,
>  	},
>  	{ /* sentinel */ }
>  };
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea4f19ede6 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -119,8 +119,19 @@
>  
>  struct regmap;
>  
> +enum adp5585_regmap_type {
> +	ADP5585_REGMAP_00,
> +	ADP5585_REGMAP_02,
> +	ADP5585_REGMAP_04,
> +};
> +
> +struct adp5585_info {
> +	enum adp5585_regmap_type regmap_type;
> +};
> +
>  struct adp5585_dev {
>  	struct regmap *regmap;
> +	const struct adp5585_info *info;
>  };
>  
>  #endif
> 
> -- 
> 2.49.0
> 
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-13 14:34   ` Lee Jones
@ 2025-05-13 15:02     ` Nuno Sá
  2025-05-13 15:19       ` Laurent Pinchart
  0 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:02 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 Tue, 2025-05-13 at 15:34 +0100, Lee Jones wrote:
> On Mon, 12 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 | 45 +++++++++++++++++++++++++++++++++++++++++----
> >  1 file changed, 41 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d69886
> > e9fb7a 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,12 +116,40 @@ static const struct regmap_config
> > adp5585_regmap_configs[] = {
> >  	},
> >  };
> >  
> > +static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > *adp5585,
> > +			    struct mfd_cell **devs)
> > +{
> > +	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> > +
> > +	if (device_property_present(dev, "#pwm-cells"))
> > +		has_pwm = 1;
> 
> This is a little sloppy.  Instead of using throwaway local variables, do
> what you're going to do in the if statement.
> 

Then I would need to realloc my device cells... But as I realized below, this is
indeed not needed.

> > +	if (device_property_present(dev, "#gpio-cells"))
> > +		has_gpio = 1;
> > +
> > +	if (!has_pwm && !has_gpio)
> > +		return -ENODEV;
> 
> Are we really dictating which child devices to register based on random
> DT properties?  Why not register them anyway and have them fail if the
> information they need is not available?  Missing / incorrect properties
> usually get a -EINVAL.

Well, this was something Laurent asked for... In the previous version I was
registering all the devices unconditionally.
 
> 
> > +	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct
> > mfd_cell),
> > +			     GFP_KERNEL);
> > +	if (!*devs)
> > +		return -ENOMEM;
> > +
> > +	if (has_pwm)
> > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
> > +	if (has_gpio)
> > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
> 
> Passing around pointers to pointers for allocation (and later, pointer
> to functions) is not the way we wish to operate.  See how all of the
> other MFD drivers handle selective sub-drivers.

Any pointer from the top of your head (example driver)? Honestly, I do not see
this being that bad. Pretty much is a dynamic array of struct mfd_cel but
anyways, no strong feelings

But... I was actually being very stupid. First I did looked at an API to only
add one mfd device and failed to realize that I can use devm_mfd_add_devices()
with n_devs = 1

Nevermind, will refactor in v4

> 
> > +	return rc;
> > +}
> > +
> >  static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  {
> >  	const struct regmap_config *regmap_config;
> >  	struct adp5585_dev *adp5585;
> > +	struct mfd_cell *devs;
> >  	unsigned int id;
> > -	int ret;
> > +	int ret, n_devs;
> >  
> >  	adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585), GFP_KERNEL);
> >  	if (!adp5585)
> > @@ -138,9 +172,12 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  		return dev_err_probe(&i2c->dev, -ENODEV,
> >  				     "Invalid device ID 0x%02x\n", id);
> >  
> > +	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> > +	if (n_devs < 0)
> > +		return n_devs;
> > +
> >  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > -				   adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > -				   NULL, 0, NULL);
> > +				   devs, n_devs, NULL, 0, NULL);
> >  	if (ret)
> >  		return dev_err_probe(&i2c->dev, ret,
> >  				     "Failed to add child devices\n");
> > 
> > -- 
> > 2.49.0
> > 
> > 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:00   ` Lee Jones
@ 2025-05-13 15:02     ` Lee Jones
  2025-05-13 15:32     ` Nuno Sá
  2025-05-13 15:35     ` Laurent Pinchart
  2 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 15:02 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 Tue, 13 May 2025, Lee Jones wrote:

> On Mon, 12 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
> > (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       | 94 +++++++++++++++++++++++++--------------------
> >  include/linux/mfd/adp5585.h | 11 ++++++
> >  2 files changed, 64 insertions(+), 41 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c19667af 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -81,41 +81,34 @@ 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,
> > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > +				      struct regmap_config *regmap_config)
> 
> I like the general idea.  This is much more scaleable than before.
> 
> > +{
> > +	*regmap_config = adp5585_regmap_config_template;
> > +
> > +	switch (adp5585->info->regmap_type) {
> > +	case ADP5585_REGMAP_00:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_00;
> > +		return 0;
> > +	case ADP5585_REGMAP_02:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_02;
> > +		return 0;
> > +	case ADP5585_REGMAP_04:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_04;
> 
> You could make this read a tiny bit nicer (as you do with the adp5585->info
> in a later patch) and make reg_defaults_raw a local variable.
> 
> > +		return 0;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +}
> >  
> >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> >  			    struct mfd_cell **devs)
> > @@ -153,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;
> >  	struct mfd_cell *devs;
> >  	unsigned int id;
> > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > +	if (!adp5585->info)
> > +		return -ENODEV;
> > +
> > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > +	if (ret)
> > +		return ret;
> > +
> > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> >  	if (IS_ERR(adp5585->regmap))
> >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> >  				     "Failed to initialize register map\n");
> > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> >  
> >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
> >  
> > +static struct adp5585_info adp5585_info = {
> > +	.regmap_type = ADP5585_REGMAP_00,
> 
> Instead of providing this enum, then later another one (id) which is a
> subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> through the DT .data attribute then match on those?  It will add a
> couple of lines to the switch(info->id) statement, but will save on a
> boat load of static structs and other complexity.
> 
> For instance:
> 
> switch (info->id) {
> 	case ADP5585_MAN_ID_VALUE:
> 
> Would simply become:
> 
> switch (info->id) {
> 	case ADP5585_REGMAP_00:
> 	case ADP5585_REGMAP_02:
> 	case ADP5585_REGMAP_04:
> 
> And that's it.
> 
> > +};
> > +
> > +static struct adp5585_info adp5585_02_info = {
> > +	.regmap_type = ADP5585_REGMAP_02,
> > +};
> > +
> > +static struct adp5585_info adp5585_04_info = {
> > +	.regmap_type = ADP5585_REGMAP_04,
> > +};
> > +
> >  static const struct of_device_id adp5585_of_match[] = {
> >  	{
> >  		.compatible = "adi,adp5585-00",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> 
> 		.data = ADP5585_REGMAP_00,

It goes without saying that the defines would have to become more
generic as well, since they will be expanded to describe more than just
'REGMAP'.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-13 15:02     ` Nuno Sá
@ 2025-05-13 15:19       ` Laurent Pinchart
  2025-05-13 15:37         ` Nuno Sá
  2025-05-13 16:12         ` Lee Jones
  0 siblings, 2 replies; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-13 15:19 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Lee Jones, 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, Liu Ying

On Tue, May 13, 2025 at 04:02:11PM +0100, Nuno Sá wrote:
> On Tue, 2025-05-13 at 15:34 +0100, Lee Jones wrote:
> > On Mon, 12 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 | 45 +++++++++++++++++++++++++++++++++++++++++----
> > >  1 file changed, 41 insertions(+), 4 deletions(-)
> > > 
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d69886
> > > e9fb7a 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,12 +116,40 @@ static const struct regmap_config
> > > adp5585_regmap_configs[] = {
> > >  	},
> > >  };
> > >  
> > > +static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > *adp5585,
> > > +			    struct mfd_cell **devs)
> > > +{
> > > +	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> > > +
> > > +	if (device_property_present(dev, "#pwm-cells"))
> > > +		has_pwm = 1;
> > 
> > This is a little sloppy.  Instead of using throwaway local variables, do
> > what you're going to do in the if statement.
> 
> Then I would need to realloc my device cells... But as I realized below, this is
> indeed not needed.
> 
> > > +	if (device_property_present(dev, "#gpio-cells"))
> > > +		has_gpio = 1;
> > > +
> > > +	if (!has_pwm && !has_gpio)
> > > +		return -ENODEV;
> > 
> > Are we really dictating which child devices to register based on random
> > DT properties?  Why not register them anyway and have them fail if the

The properties are not random.

> > information they need is not available?  Missing / incorrect properties
> > usually get a -EINVAL.
> 
> Well, this was something Laurent asked for... In the previous version I was
> registering all the devices unconditionally.

Registering them all means we'll get error messages in the kernel log
when the corresponding drivers will probe, while nothing is actually
wrong. That's fairly confusing for the user.

In an ideal situation we would have child nodes in DT and only register
child devices for existing child nodes. Unfortunately the DT bindings
were not designed that way, so we have to live with the current
situation.

> > > +	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
> > > +			     GFP_KERNEL);
> > > +	if (!*devs)
> > > +		return -ENOMEM;
> > > +
> > > +	if (has_pwm)
> > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
> > > +	if (has_gpio)
> > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
> > 
> > Passing around pointers to pointers for allocation (and later, pointer
> > to functions) is not the way we wish to operate.  See how all of the
> > other MFD drivers handle selective sub-drivers.
> 
> Any pointer from the top of your head (example driver)? Honestly, I do not see
> this being that bad. Pretty much is a dynamic array of struct mfd_cel but
> anyways, no strong feelings

I don't find it that bad either. I don't think you should use
devm_kcalloc() though, as the memory should be freed as soon as it's not
needed anymore.

> But... I was actually being very stupid. First I did looked at an API to only

Occasionally overseeing a possible solution isn't being stupid. Or at
least I hope it isn't, otherwise I would be very stupid too.

> add one mfd device and failed to realize that I can use devm_mfd_add_devices()
> with n_devs = 1
> 
> Nevermind, will refactor in v4
> 
> > > +	return rc;
> > > +}
> > > +
> > >  static int adp5585_i2c_probe(struct i2c_client *i2c)
> > >  {
> > >  	const struct regmap_config *regmap_config;
> > >  	struct adp5585_dev *adp5585;
> > > +	struct mfd_cell *devs;
> > >  	unsigned int id;
> > > -	int ret;
> > > +	int ret, n_devs;
> > >  
> > >  	adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585), GFP_KERNEL);
> > >  	if (!adp5585)
> > > @@ -138,9 +172,12 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > >  		return dev_err_probe(&i2c->dev, -ENODEV,
> > >  				     "Invalid device ID 0x%02x\n", id);
> > >  
> > > +	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> > > +	if (n_devs < 0)
> > > +		return n_devs;
> > > +
> > >  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > > -				   adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > > -				   NULL, 0, NULL);
> > > +				   devs, n_devs, NULL, 0, NULL);
> > >  	if (ret)
> > >  		return dev_err_probe(&i2c->dev, ret,
> > >  				     "Failed to add child devices\n");
> > > 

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-12 12:38 ` [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
  2025-05-13 14:26   ` Lee Jones
@ 2025-05-13 15:24   ` Laurent Pinchart
  2025-05-13 15:38     ` Nuno Sá
  1 sibling, 1 reply; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-13 15:24 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

Hi Nuno,

Thank you for the patch.

On Mon, May 12, 2025 at 01:38:55PM +0100, 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).
> 
> 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 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab3b3395 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  	return rc;
>  }
>  
> +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;
> @@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  	if (n_devs < 0)
>  		return n_devs;
>  

Could you add a comment here to explain what's going on ? Something
along the lines of

	/*
	 * 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.
	 */

With that,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +	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;
> +
>  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
>  				   devs, n_devs, NULL, 0, NULL);
>  	if (ret)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver
  2025-05-12 12:38 ` [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
@ 2025-05-13 15:26   ` Laurent Pinchart
  2025-05-13 15:39     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-13 15:26 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

Hi Nuno,

Thank you for the patch.

On Mon, May 12, 2025 at 01:38:56PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
> 
> The adp5585 is a Multi Function Device that can also be a gpio
> controller and as it turns out, when OSC_EN is not set, we can't
> reliably read the gpio value when it's configured as input. Hence,
> OSC_EN will be set during probe by the parent device (and cleared on
> unbind).
> 
> Moreover, we'll add support for the keymap matrix (input device) which
> definitely needs OSC_EN to be set and so, we cannot afford that disabling
> the PWM output also breaks the keymap events generation.

I think you can squash this with 03/22 if you send a new version. Moving
the OSC_EN bit handling from the PWM child driver to the MFD driver is a
single logical change.

> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
>  drivers/pwm/pwm-adp5585.c | 5 -----
>  1 file changed, 5 deletions(-)
> 
> 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);
>  }
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:00   ` Lee Jones
  2025-05-13 15:02     ` Lee Jones
@ 2025-05-13 15:32     ` Nuno Sá
  2025-05-13 15:36       ` Laurent Pinchart
  2025-05-13 15:35     ` Laurent Pinchart
  2 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:32 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 Tue, 2025-05-13 at 16:00 +0100, Lee Jones wrote:
> On Mon, 12 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
> > (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       | 94 +++++++++++++++++++++++++-----------------
> > ---
> >  include/linux/mfd/adp5585.h | 11 ++++++
> >  2 files changed, 64 insertions(+), 41 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c1
> > 9667af 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -81,41 +81,34 @@ 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,
> > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > +				      struct regmap_config *regmap_config)
> 
> I like the general idea.  This is much more scaleable than before.
> 
> > +{
> > +	*regmap_config = adp5585_regmap_config_template;
> > +
> > +	switch (adp5585->info->regmap_type) {
> > +	case ADP5585_REGMAP_00:
> > +		regmap_config->reg_defaults_raw =
> > adp5585_regmap_defaults_00;
> > +		return 0;
> > +	case ADP5585_REGMAP_02:
> > +		regmap_config->reg_defaults_raw =
> > adp5585_regmap_defaults_02;
> > +		return 0;
> > +	case ADP5585_REGMAP_04:
> > +		regmap_config->reg_defaults_raw =
> > adp5585_regmap_defaults_04;
> 
> You could make this read a tiny bit nicer (as you do with the adp5585->info
> in a later patch) and make reg_defaults_raw a local variable.

I'm probably missing your point but what would be the benefit? The info is done
like that because I wanted the pointer to be 'const'. Here I do not think the
same applies...

> 
> > +		return 0;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +}
> >  
> >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > *adp5585,
> >  			    struct mfd_cell **devs)
> > @@ -153,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;
> >  	struct mfd_cell *devs;
> >  	unsigned int id;
> > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > +	if (!adp5585->info)
> > +		return -ENODEV;
> > +
> > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > +	if (ret)
> > +		return ret;
> > +
> > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> >  	if (IS_ERR(adp5585->regmap))
> >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> >  				     "Failed to initialize register
> > map\n");
> > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> >  
> >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend,
> > adp5585_resume);
> >  
> > +static struct adp5585_info adp5585_info = {
> > +	.regmap_type = ADP5585_REGMAP_00,
> 
> Instead of providing this enum, then later another one (id) which is a
> subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> through the DT .data attribute then match on those?  It will add a
> couple of lines to the switch(info->id) statement, but will save on a
> boat load of static structs and other complexity.
> 
> For instance:
> 
> switch (info->id) {
> 	case ADP5585_MAN_ID_VALUE:
> 
> Would simply become:
> 
> switch (info->id) {
> 	case ADP5585_REGMAP_00:
> 	case ADP5585_REGMAP_02:
> 	case ADP5585_REGMAP_04:
> 
> And that's it.

I get the general idea... We will also have to pack the regmap defaults into an
array so that we can easily reference it with 'info->id' which I don't like too
much tbh (but I do see that adp5585_fill_chip_configs() will become simpler) . I
guess I can also just move everything into the "main" struct as we will fill
everything during probe (no real reason for struct adp5585_info) 

Anyways, If you prefer the above I'm not going to argue against it...

> 
> > +};
> > +
> > +static struct adp5585_info adp5585_02_info = {
> > +	.regmap_type = ADP5585_REGMAP_02,
> > +};
> > +
> > +static struct adp5585_info adp5585_04_info = {
> > +	.regmap_type = ADP5585_REGMAP_04,
> > +};
> > +
> >  static const struct of_device_id adp5585_of_match[] = {
> >  	{
> >  		.compatible = "adi,adp5585-00",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> 
> 		.data = ADP5585_REGMAP_00,

I see, needs a cast but should work. I personally prefer valid pointers than
"encoding" integers in here. I know we can start the enum at 1 so that we can
still look for 0 for any possible issue but...


> 
> >  	}, {
> >  		.compatible = "adi,adp5585-01",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-02",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > +		.data = &adp5585_02_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-03",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-04",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > +		.data = &adp5585_04_info,
> >  	},
> >  	{ /* sentinel */ }
> >  };
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index
> > 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea4f
> > 19ede6 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -119,8 +119,19 @@
> >  
> >  struct regmap;
> >  
> > +enum adp5585_regmap_type {
> > +	ADP5585_REGMAP_00,
> > +	ADP5585_REGMAP_02,
> > +	ADP5585_REGMAP_04,
> > +};
> > +
> > +struct adp5585_info {
> > +	enum adp5585_regmap_type regmap_type;
> > +};
> > +
> >  struct adp5585_dev {
> >  	struct regmap *regmap;
> > +	const struct adp5585_info *info;
> >  };
> >  
> >  #endif
> > 
> > -- 
> > 2.49.0
> > 
> > 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:00   ` Lee Jones
  2025-05-13 15:02     ` Lee Jones
  2025-05-13 15:32     ` Nuno Sá
@ 2025-05-13 15:35     ` Laurent Pinchart
  2025-05-13 15:41       ` Nuno Sá
  2 siblings, 1 reply; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-13 15:35 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, Liu Ying

On Tue, May 13, 2025 at 04:00:29PM +0100, Lee Jones wrote:
> On Mon, 12 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
> > (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       | 94 +++++++++++++++++++++++++--------------------
> >  include/linux/mfd/adp5585.h | 11 ++++++
> >  2 files changed, 64 insertions(+), 41 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c19667af 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -81,41 +81,34 @@ 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,
> > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > +				      struct regmap_config *regmap_config)
> 
> I like the general idea.  This is much more scaleable than before.
> 
> > +{
> > +	*regmap_config = adp5585_regmap_config_template;
> > +
> > +	switch (adp5585->info->regmap_type) {
> > +	case ADP5585_REGMAP_00:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_00;
> > +		return 0;
> > +	case ADP5585_REGMAP_02:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_02;
> > +		return 0;
> > +	case ADP5585_REGMAP_04:
> > +		regmap_config->reg_defaults_raw = adp5585_regmap_defaults_04;
> 
> You could make this read a tiny bit nicer (as you do with the adp5585->info
> in a later patch) and make reg_defaults_raw a local variable.

And as ADP585_REGMAP_* is an enum and we have to handle all values, you
can replace the switch with a static const array lookup.

> > +		return 0;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +}
> >  
> >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> >  			    struct mfd_cell **devs)
> > @@ -153,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;
> >  	struct mfd_cell *devs;
> >  	unsigned int id;
> > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > +	if (!adp5585->info)
> > +		return -ENODEV;
> > +
> > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > +	if (ret)
> > +		return ret;
> > +
> > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> >  	if (IS_ERR(adp5585->regmap))
> >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> >  				     "Failed to initialize register map\n");
> > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> >  
> >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
> >  
> > +static struct adp5585_info adp5585_info = {
> > +	.regmap_type = ADP5585_REGMAP_00,
> 
> Instead of providing this enum, then later another one (id) which is a
> subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> through the DT .data attribute then match on those?  It will add a
> couple of lines to the switch(info->id) statement, but will save on a
> boat load of static structs and other complexity.
> 
> For instance:
> 
> switch (info->id) {
> 	case ADP5585_MAN_ID_VALUE:
> 
> Would simply become:
> 
> switch (info->id) {
> 	case ADP5585_REGMAP_00:
> 	case ADP5585_REGMAP_02:
> 	case ADP5585_REGMAP_04:
> 
> And that's it.
> 
> > +};
> > +
> > +static struct adp5585_info adp5585_02_info = {
> > +	.regmap_type = ADP5585_REGMAP_02,
> > +};
> > +
> > +static struct adp5585_info adp5585_04_info = {
> > +	.regmap_type = ADP5585_REGMAP_04,
> > +};
> > +
> >  static const struct of_device_id adp5585_of_match[] = {
> >  	{
> >  		.compatible = "adi,adp5585-00",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> 
> 		.data = ADP5585_REGMAP_00,
> 
> >  	}, {
> >  		.compatible = "adi,adp5585-01",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-02",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > +		.data = &adp5585_02_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-03",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > +		.data = &adp5585_info,
> >  	}, {
> >  		.compatible = "adi,adp5585-04",
> > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > +		.data = &adp5585_04_info,
> >  	},
> >  	{ /* sentinel */ }
> >  };
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea4f19ede6 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -119,8 +119,19 @@
> >  
> >  struct regmap;
> >  
> > +enum adp5585_regmap_type {
> > +	ADP5585_REGMAP_00,
> > +	ADP5585_REGMAP_02,
> > +	ADP5585_REGMAP_04,
> > +};
> > +
> > +struct adp5585_info {
> > +	enum adp5585_regmap_type regmap_type;
> > +};
> > +
> >  struct adp5585_dev {
> >  	struct regmap *regmap;
> > +	const struct adp5585_info *info;
> >  };
> >  
> >  #endif

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:32     ` Nuno Sá
@ 2025-05-13 15:36       ` Laurent Pinchart
  2025-05-13 16:03         ` Lee Jones
  0 siblings, 1 reply; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-13 15:36 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Lee Jones, 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, Liu Ying

On Tue, May 13, 2025 at 04:32:39PM +0100, Nuno Sá wrote:
> On Tue, 2025-05-13 at 16:00 +0100, Lee Jones wrote:
> > On Mon, 12 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
> > > (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       | 94 +++++++++++++++++++++++++-----------------
> > > ---
> > >  include/linux/mfd/adp5585.h | 11 ++++++
> > >  2 files changed, 64 insertions(+), 41 deletions(-)
> > > 
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c1
> > > 9667af 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -81,41 +81,34 @@ 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,
> > > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > > +				      struct regmap_config *regmap_config)
> > 
> > I like the general idea.  This is much more scaleable than before.
> > 
> > > +{
> > > +	*regmap_config = adp5585_regmap_config_template;
> > > +
> > > +	switch (adp5585->info->regmap_type) {
> > > +	case ADP5585_REGMAP_00:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_00;
> > > +		return 0;
> > > +	case ADP5585_REGMAP_02:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_02;
> > > +		return 0;
> > > +	case ADP5585_REGMAP_04:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_04;
> > 
> > You could make this read a tiny bit nicer (as you do with the adp5585->info
> > in a later patch) and make reg_defaults_raw a local variable.
> 
> I'm probably missing your point but what would be the benefit? The info is done
> like that because I wanted the pointer to be 'const'. Here I do not think the
> same applies...
> 
> > 
> > > +		return 0;
> > > +	default:
> > > +		return -ENODEV;
> > > +	}
> > > +}
> > >  
> > >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > *adp5585,
> > >  			    struct mfd_cell **devs)
> > > @@ -153,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;
> > >  	struct mfd_cell *devs;
> > >  	unsigned int id;
> > > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > > +	if (!adp5585->info)
> > > +		return -ENODEV;
> > > +
> > > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> > >  	if (IS_ERR(adp5585->regmap))
> > >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > >  				     "Failed to initialize register
> > > map\n");
> > > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> > >  
> > >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend,
> > > adp5585_resume);
> > >  
> > > +static struct adp5585_info adp5585_info = {
> > > +	.regmap_type = ADP5585_REGMAP_00,
> > 
> > Instead of providing this enum, then later another one (id) which is a
> > subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> > through the DT .data attribute then match on those?  It will add a
> > couple of lines to the switch(info->id) statement, but will save on a
> > boat load of static structs and other complexity.
> > 
> > For instance:
> > 
> > switch (info->id) {
> > 	case ADP5585_MAN_ID_VALUE:
> > 
> > Would simply become:
> > 
> > switch (info->id) {
> > 	case ADP5585_REGMAP_00:
> > 	case ADP5585_REGMAP_02:
> > 	case ADP5585_REGMAP_04:
> > 
> > And that's it.
> 
> I get the general idea... We will also have to pack the regmap defaults into an
> array so that we can easily reference it with 'info->id' which I don't like too
> much tbh (but I do see that adp5585_fill_chip_configs() will become simpler) . I
> guess I can also just move everything into the "main" struct as we will fill
> everything during probe (no real reason for struct adp5585_info) 
> 
> Anyways, If you prefer the above I'm not going to argue against it...
> 
> > 
> > > +};
> > > +
> > > +static struct adp5585_info adp5585_02_info = {
> > > +	.regmap_type = ADP5585_REGMAP_02,
> > > +};
> > > +
> > > +static struct adp5585_info adp5585_04_info = {
> > > +	.regmap_type = ADP5585_REGMAP_04,
> > > +};
> > > +
> > >  static const struct of_device_id adp5585_of_match[] = {
> > >  	{
> > >  		.compatible = "adi,adp5585-00",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > 
> > 		.data = ADP5585_REGMAP_00,
> 
> I see, needs a cast but should work. I personally prefer valid pointers than
> "encoding" integers in here. I know we can start the enum at 1 so that we can
> still look for 0 for any possible issue but...

I also prefer pointers to structures, but won't fight for it.

> > >  	}, {
> > >  		.compatible = "adi,adp5585-01",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-02",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > > +		.data = &adp5585_02_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-03",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-04",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > > +		.data = &adp5585_04_info,
> > >  	},
> > >  	{ /* sentinel */ }
> > >  };
> > > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > > index
> > > 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea4f
> > > 19ede6 100644
> > > --- a/include/linux/mfd/adp5585.h
> > > +++ b/include/linux/mfd/adp5585.h
> > > @@ -119,8 +119,19 @@
> > >  
> > >  struct regmap;
> > >  
> > > +enum adp5585_regmap_type {
> > > +	ADP5585_REGMAP_00,
> > > +	ADP5585_REGMAP_02,
> > > +	ADP5585_REGMAP_04,
> > > +};
> > > +
> > > +struct adp5585_info {
> > > +	enum adp5585_regmap_type regmap_type;
> > > +};
> > > +
> > >  struct adp5585_dev {
> > >  	struct regmap *regmap;
> > > +	const struct adp5585_info *info;
> > >  };
> > >  
> > >  #endif

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-13 15:19       ` Laurent Pinchart
@ 2025-05-13 15:37         ` Nuno Sá
  2025-05-13 16:12         ` Lee Jones
  1 sibling, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:37 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Lee Jones, 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, Liu Ying

On Tue, 2025-05-13 at 17:19 +0200, Laurent Pinchart wrote:
> On Tue, May 13, 2025 at 04:02:11PM +0100, Nuno Sá wrote:
> > On Tue, 2025-05-13 at 15:34 +0100, Lee Jones wrote:
> > > On Mon, 12 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 | 45 +++++++++++++++++++++++++++++++++++++++++---
> > > > -
> > > >  1 file changed, 41 insertions(+), 4 deletions(-)
> > > > 
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d6
> > > > 9886
> > > > e9fb7a 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,12 +116,40 @@ static const struct regmap_config
> > > > adp5585_regmap_configs[] = {
> > > >  	},
> > > >  };
> > > >  
> > > > +static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > > *adp5585,
> > > > +			    struct mfd_cell **devs)
> > > > +{
> > > > +	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> > > > +
> > > > +	if (device_property_present(dev, "#pwm-cells"))
> > > > +		has_pwm = 1;
> > > 
> > > This is a little sloppy.  Instead of using throwaway local variables, do
> > > what you're going to do in the if statement.
> > 
> > Then I would need to realloc my device cells... But as I realized below,
> > this is
> > indeed not needed.
> > 
> > > > +	if (device_property_present(dev, "#gpio-cells"))
> > > > +		has_gpio = 1;
> > > > +
> > > > +	if (!has_pwm && !has_gpio)
> > > > +		return -ENODEV;
> > > 
> > > Are we really dictating which child devices to register based on random
> > > DT properties?  Why not register them anyway and have them fail if the
> 
> The properties are not random.
> 
> > > information they need is not available?  Missing / incorrect properties
> > > usually get a -EINVAL.
> > 
> > Well, this was something Laurent asked for... In the previous version I was
> > registering all the devices unconditionally.
> 
> Registering them all means we'll get error messages in the kernel log
> when the corresponding drivers will probe, while nothing is actually
> wrong. That's fairly confusing for the user.
> 
> In an ideal situation we would have child nodes in DT and only register
> child devices for existing child nodes. Unfortunately the DT bindings
> were not designed that way, so we have to live with the current
> situation.
> 
> > > > +	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct
> > > > mfd_cell),
> > > > +			     GFP_KERNEL);
> > > > +	if (!*devs)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	if (has_pwm)
> > > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
> > > > +	if (has_gpio)
> > > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
> > > 
> > > Passing around pointers to pointers for allocation (and later, pointer
> > > to functions) is not the way we wish to operate.  See how all of the
> > > other MFD drivers handle selective sub-drivers.
> > 
> > Any pointer from the top of your head (example driver)? Honestly, I do not
> > see
> > this being that bad. Pretty much is a dynamic array of struct mfd_cel but
> > anyways, no strong feelings
> 
> I don't find it that bad either. I don't think you should use
> devm_kcalloc() though, as the memory should be freed as soon as it's not
> needed anymore.
> 

Agreed... Sometimes I do forget we also have __free(kfree) even though I used it
in the keyboard driver.

> > But... I was actually being very stupid. First I did looked at an API to
> > only
> 
> Occasionally overseeing a possible solution isn't being stupid. Or at
> least I hope it isn't, otherwise I would be very stupid too.
> 

I know :)... 

> > add one mfd device and failed to realize that I can use
> > devm_mfd_add_devices()
> > with n_devs = 1
> > 
> > Nevermind, will refactor in v4
> > 
> > > > +	return rc;
> > > > +}
> > > > +
> > > >  static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > >  {
> > > >  	const struct regmap_config *regmap_config;
> > > >  	struct adp5585_dev *adp5585;
> > > > +	struct mfd_cell *devs;
> > > >  	unsigned int id;
> > > > -	int ret;
> > > > +	int ret, n_devs;
> > > >  
> > > >  	adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585),
> > > > GFP_KERNEL);
> > > >  	if (!adp5585)
> > > > @@ -138,9 +172,12 @@ static int adp5585_i2c_probe(struct i2c_client
> > > > *i2c)
> > > >  		return dev_err_probe(&i2c->dev, -ENODEV,
> > > >  				     "Invalid device ID 0x%02x\n", id);
> > > >  
> > > > +	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> > > > +	if (n_devs < 0)
> > > > +		return n_devs;
> > > > +
> > > >  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > > > -				   adp5585_devs,
> > > > ARRAY_SIZE(adp5585_devs),
> > > > -				   NULL, 0, NULL);
> > > > +				   devs, n_devs, NULL, 0, NULL);
> > > >  	if (ret)
> > > >  		return dev_err_probe(&i2c->dev, ret,
> > > >  				     "Failed to add child devices\n");
> > > > 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-13 15:24   ` Laurent Pinchart
@ 2025-05-13 15:38     ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:38 UTC (permalink / raw)
  To: Laurent Pinchart, nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

On Tue, 2025-05-13 at 17:24 +0200, Laurent Pinchart wrote:
> Hi Nuno,
> 
> Thank you for the patch.
> 
> On Mon, May 12, 2025 at 01:38:55PM +0100, 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).
> > 
> > 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
> > 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab
> > 3b3395 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  	return rc;
> >  }
> >  
> > +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;
> > @@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  	if (n_devs < 0)
> >  		return n_devs;
> >  
> 
> Could you add a comment here to explain what's going on ? Something
> along the lines of
> 
> 	/*
> 	 * 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.
> 	 */
> 

Sure, I can add a similar remark in the regulator patch

Thanks!

> With that,
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> > +	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;
> > +
> >  	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> >  				   devs, n_devs, NULL, 0, NULL);
> >  	if (ret)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver
  2025-05-13 15:26   ` Laurent Pinchart
@ 2025-05-13 15:39     ` Nuno Sá
  2025-05-13 16:04       ` Lee Jones
  0 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:39 UTC (permalink / raw)
  To: Laurent Pinchart, nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

On Tue, 2025-05-13 at 17:26 +0200, Laurent Pinchart wrote:
> Hi Nuno,
> 
> Thank you for the patch.
> 
> On Mon, May 12, 2025 at 01:38:56PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> > 
> > The adp5585 is a Multi Function Device that can also be a gpio
> > controller and as it turns out, when OSC_EN is not set, we can't
> > reliably read the gpio value when it's configured as input. Hence,
> > OSC_EN will be set during probe by the parent device (and cleared on
> > unbind).
> > 
> > Moreover, we'll add support for the keymap matrix (input device) which
> > definitely needs OSC_EN to be set and so, we cannot afford that disabling
> > the PWM output also breaks the keymap events generation.
> 
> I think you can squash this with 03/22 if you send a new version. Moving
> the OSC_EN bit handling from the PWM child driver to the MFD driver is a
> single logical change.

Will do... happy to reduce the number of patches in the series

> 
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> >  drivers/pwm/pwm-adp5585.c | 5 -----
> >  1 file changed, 5 deletions(-)
> > 
> > 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);
> >  }
> >  

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:35     ` Laurent Pinchart
@ 2025-05-13 15:41       ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-13 15:41 UTC (permalink / raw)
  To: Laurent Pinchart, 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, Liu Ying

On Tue, 2025-05-13 at 17:35 +0200, Laurent Pinchart wrote:
> On Tue, May 13, 2025 at 04:00:29PM +0100, Lee Jones wrote:
> > On Mon, 12 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
> > > (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       | 94 +++++++++++++++++++++++++---------------
> > > -----
> > >  include/linux/mfd/adp5585.h | 11 ++++++
> > >  2 files changed, 64 insertions(+), 41 deletions(-)
> > > 
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995
> > > c19667af 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -81,41 +81,34 @@ 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,
> > > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > > +				      struct regmap_config
> > > *regmap_config)
> > 
> > I like the general idea.  This is much more scaleable than before.
> > 
> > > +{
> > > +	*regmap_config = adp5585_regmap_config_template;
> > > +
> > > +	switch (adp5585->info->regmap_type) {
> > > +	case ADP5585_REGMAP_00:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_00;
> > > +		return 0;
> > > +	case ADP5585_REGMAP_02:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_02;
> > > +		return 0;
> > > +	case ADP5585_REGMAP_04:
> > > +		regmap_config->reg_defaults_raw =
> > > adp5585_regmap_defaults_04;
> > 
> > You could make this read a tiny bit nicer (as you do with the adp5585->info
> > in a later patch) and make reg_defaults_raw a local variable.
> 
> And as ADP585_REGMAP_* is an enum and we have to handle all values, you
> can replace the switch with a static const array lookup.

ack

> 
> > > +		return 0;
> > > +	default:
> > > +		return -ENODEV;
> > > +	}
> > > +}
> > >  
> > >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > *adp5585,
> > >  			    struct mfd_cell **devs)
> > > @@ -153,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;
> > >  	struct mfd_cell *devs;
> > >  	unsigned int id;
> > > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > > +	if (!adp5585->info)
> > > +		return -ENODEV;
> > > +
> > > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> > >  	if (IS_ERR(adp5585->regmap))
> > >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > >  				     "Failed to initialize register
> > > map\n");
> > > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> > >  
> > >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend,
> > > adp5585_resume);
> > >  
> > > +static struct adp5585_info adp5585_info = {
> > > +	.regmap_type = ADP5585_REGMAP_00,
> > 
> > Instead of providing this enum, then later another one (id) which is a
> > subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> > through the DT .data attribute then match on those?  It will add a
> > couple of lines to the switch(info->id) statement, but will save on a
> > boat load of static structs and other complexity.
> > 
> > For instance:
> > 
> > switch (info->id) {
> > 	case ADP5585_MAN_ID_VALUE:
> > 
> > Would simply become:
> > 
> > switch (info->id) {
> > 	case ADP5585_REGMAP_00:
> > 	case ADP5585_REGMAP_02:
> > 	case ADP5585_REGMAP_04:
> > 
> > And that's it.
> > 
> > > +};
> > > +
> > > +static struct adp5585_info adp5585_02_info = {
> > > +	.regmap_type = ADP5585_REGMAP_02,
> > > +};
> > > +
> > > +static struct adp5585_info adp5585_04_info = {
> > > +	.regmap_type = ADP5585_REGMAP_04,
> > > +};
> > > +
> > >  static const struct of_device_id adp5585_of_match[] = {
> > >  	{
> > >  		.compatible = "adi,adp5585-00",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > 
> > 		.data = ADP5585_REGMAP_00,
> > 
> > >  	}, {
> > >  		.compatible = "adi,adp5585-01",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-02",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > > +		.data = &adp5585_02_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-03",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > +		.data = &adp5585_info,
> > >  	}, {
> > >  		.compatible = "adi,adp5585-04",
> > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > > +		.data = &adp5585_04_info,
> > >  	},
> > >  	{ /* sentinel */ }
> > >  };
> > > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > > index
> > > 016033cd68e46757aca86d21dd37025fd354b801..4b48614970a811a8a95116faa20e58ea
> > > 4f19ede6 100644
> > > --- a/include/linux/mfd/adp5585.h
> > > +++ b/include/linux/mfd/adp5585.h
> > > @@ -119,8 +119,19 @@
> > >  
> > >  struct regmap;
> > >  
> > > +enum adp5585_regmap_type {
> > > +	ADP5585_REGMAP_00,
> > > +	ADP5585_REGMAP_02,
> > > +	ADP5585_REGMAP_04,
> > > +};
> > > +
> > > +struct adp5585_info {
> > > +	enum adp5585_regmap_type regmap_type;
> > > +};
> > > +
> > >  struct adp5585_dev {
> > >  	struct regmap *regmap;
> > > +	const struct adp5585_info *info;
> > >  };
> > >  
> > >  #endif

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 13/22] mfd: adp5585: add support for event handling
  2025-05-12 12:39 ` [PATCH v3 13/22] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
@ 2025-05-13 15:59   ` Lee Jones
  2025-05-15  5:56     ` Nuno Sá
  2025-05-15  6:14     ` Nuno Sá
  0 siblings, 2 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 15:59 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 Mon, 12 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       | 180 ++++++++++++++++++++++++++++++++++++++++++--
>  include/linux/mfd/adp5585.h |  48 ++++++++++++
>  2 files changed, 223 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 8be7a76842f639cbe90ad0eb956a7a3eef43fa3d..5851ad30e7323bbb891878167d0786bc60ef5d90 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -154,10 +154,16 @@ static const struct regmap_config adp5585_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_chip_configs(struct adp5585_dev *adp5585,
> @@ -214,6 +220,8 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  {
>  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
>  	const struct mfd_cell *cells;
> +	unsigned int prop_val;
> +	int ret;
>  
>  	if (device_property_present(dev, "#pwm-cells"))
>  		has_pwm = 1;
> @@ -224,6 +232,25 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  	if (!has_pwm && !has_gpio)
>  		return -ENODEV;
>  
> +	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
> +	if (!ret) {
> +		switch (prop_val) {
> +		case 10:
> +			fallthrough;
> +		case 20:
> +			fallthrough;
> +		case 30:
> +			fallthrough;
> +		case 40:
> +			adp5585->ev_poll_time = prop_val / 10 - 1;
> +			break;
> +		default:
> +			return dev_err_probe(dev, -EINVAL,
> +					     "Invalid value(%u) for poll-interval\n",
> +					     prop_val);
> +		}
> +	}

This all seems like a lot of code for:

	ev_poll_time = prop_val / 10 - 1;
	if (ev_poll_time > 3 || ev_poll_time < 0)
		return dev_err_probe();

> +
>  	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
>  			     GFP_KERNEL);
>  	if (!*devs)
> @@ -249,6 +276,135 @@ 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)
> +{
> +	struct adp5585_ev_handler *h;
> +	unsigned int i;
> +
> +	guard(mutex)(&adp5585->ev_lock);
> +
> +	if (list_empty(&adp5585->ev_handlers)) {
> +		dev_warn_ratelimited(adp5585->dev, "No event handlers registered\n");
> +		return;
> +	}
> +
> +	for (i = 0; i < ev_cnt; i++) {
> +		unsigned int key, key_val, key_press;
> +		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);
> +
> +		list_for_each_entry(h, &adp5585->ev_handlers, entry) {
> +			ret = h->handler(h->dev, key_val, key_press);

Rather than rolling your own call-back handler mechanism.  Are you sure
the kernel doesn't provide a generic solution for this?  For instance,
would a shared workqueue be better?  This way you could also exit IRQ
context sooner as well.

> +			if (!ret)
> +				/* handled! */

All comments should start with an upper case char.

> +				break;
> +		}
> +	}
> +}
> +
> +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");

Strange capitalisation.

> +
> +	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);

You don't want to propagate any errors?

> +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->info->regs;
> +	unsigned int reg_val, i;
> +	int ret;

The final version of this function needs some nice commentary to explain
what each step is doing.  May as well start now.

> +	for (i = 0; i < ADP5585_EV_MAX; i++) {
> +		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> +			   adp5585->ev_poll_time);
> +	if (ret)
> +		return ret;
> +
> +	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 void adp5585_irq_disable(void *data)
> +{
> +	struct adp5585_dev *adp5585 = data;
> +
> +	regmap_write(adp5585->regmap, adp5585->info->regs->int_en, 0);
> +}
> +
> +static int adp5585_irq_enable(struct i2c_client *i2c,
> +			      struct adp5585_dev *adp5585)
> +{
> +	const struct adp5585_regs *regs = adp5585->info->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... */

Uppercase char (I won't report on this again) and now silly punctuation
please...

> +	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;

What does reading status values then writing them right back do?

Commentary throughout please.

> +	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);

Feel free to use 100-chars to help with these early line breaks.

> +}
> +
>  static int adp5585_i2c_probe(struct i2c_client *i2c)
>  {
>  	struct regmap_config regmap_config;
> @@ -282,16 +438,19 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  		return dev_err_probe(&i2c->dev, -ENODEV,
>  				     "Invalid device ID 0x%02x\n", id);
>  
> +	adp5585->dev = &i2c->dev;
> +	adp5585->irq = i2c->irq;
> +	INIT_LIST_HEAD(&adp5585->ev_handlers);
> +
>  	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
>  	if (n_devs < 0)
>  		return n_devs;
>  
> -	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> -			      ADP5585_OSC_EN);
> +	ret = adp5585_setup(adp5585);
>  	if (ret)
>  		return ret;
>  
> -	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> +	ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
>  	if (ret)
>  		return ret;
>  
> @@ -301,13 +460,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  		return dev_err_probe(&i2c->dev, ret,
>  				     "Failed to add child devices\n");
>  
> -	return 0;
> +	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;
> @@ -316,11 +478,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 9a925a91c772722db559c9ec8ae334b2159ede79..218c56bed2e0304de8b81c7090386fb4e1b6c281 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -10,13 +10,23 @@
>  #define __MFD_ADP5585_H_
>  
>  #include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/device.h>
> +#include <linux/list.h>
> +#include <linux/mutex.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 +42,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 +115,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 +134,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
>  
> @@ -138,8 +153,18 @@ enum adp5585_regmap_type {
>  	ADP5589_REGMAP_02,
>  };
>  
> +struct adp5585_ev_handler {
> +	struct list_head entry;
> +	struct device *dev;
> +	int (*handler)(struct device *dev, unsigned int key_val,
> +		       unsigned int key_press);

Pointer to functions outside of subsystem-level ops are generally
frowned upon.  Are you sure there isn't a standard way to achieve your
goal without them?

> +};
> +
>  struct adp5585_regs {
> +	unsigned int gen_cfg;
>  	unsigned int ext_cfg;
> +	unsigned int int_en;
> +	unsigned int poll_ptime_cfg;
>  };
>  
>  struct adp5585_info {
> @@ -150,7 +175,30 @@ struct adp5585_info {
>  
>  struct adp5585_dev {
>  	struct regmap *regmap;
> +	struct device *dev;
>  	const struct adp5585_info *info;
> +	/* Used to synchronize the availability of the event handlers */
> +	struct mutex ev_lock;
> +	struct list_head ev_handlers;
> +	int irq;
> +	unsigned int ev_poll_time;
>  };
>  
> +static inline void adp5585_ev_handler_remove(void *data)
> +{
> +	struct adp5585_ev_handler *handler = data;
> +	struct adp5585_dev *adp5585 = dev_get_drvdata(handler->dev->parent);
> +
> +	guard(mutex)(&adp5585->ev_lock);
> +	list_del(&handler->entry);
> +}
> +
> +static inline int devm_adp5585_ev_handler_add(struct adp5585_dev *adp5585,
> +					      struct adp5585_ev_handler *handler)
> +{
> +	guard(mutex)(&adp5585->ev_lock);
> +	list_add_tail(&handler->entry, &adp5585->ev_handlers);
> +	return devm_add_action_or_reset(handler->dev, adp5585_ev_handler_remove,
> +					handler);
> +}
>  #endif
> 
> -- 
> 2.49.0
> 
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled
  2025-05-13 15:36       ` Laurent Pinchart
@ 2025-05-13 16:03         ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16:03 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Nuno Sá, 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, Liu Ying

On Tue, 13 May 2025, Laurent Pinchart wrote:

> On Tue, May 13, 2025 at 04:32:39PM +0100, Nuno Sá wrote:
> > On Tue, 2025-05-13 at 16:00 +0100, Lee Jones wrote:
> > > On Mon, 12 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
> > > > (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       | 94 +++++++++++++++++++++++++-----------------
> > > > ---
> > > >  include/linux/mfd/adp5585.h | 11 ++++++
> > > >  2 files changed, 64 insertions(+), 41 deletions(-)
> > > > 
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > 19d4a0ab1bb4c261e82559630624059529765fbd..874aed7d7cfe052587720d899096c995c1
> > > > 9667af 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -81,41 +81,34 @@ 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,
> > > > +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 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 int adp5585_fill_regmap_config(const struct adp5585_dev *adp5585,
> > > > +				      struct regmap_config *regmap_config)
> > > 
> > > I like the general idea.  This is much more scaleable than before.
> > > 
> > > > +{
> > > > +	*regmap_config = adp5585_regmap_config_template;
> > > > +
> > > > +	switch (adp5585->info->regmap_type) {
> > > > +	case ADP5585_REGMAP_00:
> > > > +		regmap_config->reg_defaults_raw =
> > > > adp5585_regmap_defaults_00;
> > > > +		return 0;
> > > > +	case ADP5585_REGMAP_02:
> > > > +		regmap_config->reg_defaults_raw =
> > > > adp5585_regmap_defaults_02;
> > > > +		return 0;
> > > > +	case ADP5585_REGMAP_04:
> > > > +		regmap_config->reg_defaults_raw =
> > > > adp5585_regmap_defaults_04;
> > > 
> > > You could make this read a tiny bit nicer (as you do with the adp5585->info
> > > in a later patch) and make reg_defaults_raw a local variable.
> > 
> > I'm probably missing your point but what would be the benefit? The info is done
> > like that because I wanted the pointer to be 'const'. Here I do not think the
> > same applies...
> > 
> > > 
> > > > +		return 0;
> > > > +	default:
> > > > +		return -ENODEV;
> > > > +	}
> > > > +}
> > > >  
> > > >  static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > > *adp5585,
> > > >  			    struct mfd_cell **devs)
> > > > @@ -153,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;
> > > >  	struct mfd_cell *devs;
> > > >  	unsigned int id;
> > > > @@ -165,8 +158,15 @@ 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->info = i2c_get_match_data(i2c);
> > > > +	if (!adp5585->info)
> > > > +		return -ENODEV;
> > > > +
> > > > +	ret = adp5585_fill_regmap_config(adp5585, &regmap_config);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> > > >  	if (IS_ERR(adp5585->regmap))
> > > >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > > >  				     "Failed to initialize register
> > > > map\n");
> > > > @@ -223,22 +223,34 @@ static int adp5585_resume(struct device *dev)
> > > >  
> > > >  static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend,
> > > > adp5585_resume);
> > > >  
> > > > +static struct adp5585_info adp5585_info = {
> > > > +	.regmap_type = ADP5585_REGMAP_00,
> > > 
> > > Instead of providing this enum, then later another one (id) which is a
> > > subset of the same thing, why not pass just ADP5585_REGMAP_00, etc
> > > through the DT .data attribute then match on those?  It will add a
> > > couple of lines to the switch(info->id) statement, but will save on a
> > > boat load of static structs and other complexity.
> > > 
> > > For instance:
> > > 
> > > switch (info->id) {
> > > 	case ADP5585_MAN_ID_VALUE:
> > > 
> > > Would simply become:
> > > 
> > > switch (info->id) {
> > > 	case ADP5585_REGMAP_00:
> > > 	case ADP5585_REGMAP_02:
> > > 	case ADP5585_REGMAP_04:
> > > 
> > > And that's it.
> > 
> > I get the general idea... We will also have to pack the regmap defaults into an
> > array so that we can easily reference it with 'info->id' which I don't like too
> > much tbh (but I do see that adp5585_fill_chip_configs() will become simpler) . I
> > guess I can also just move everything into the "main" struct as we will fill
> > everything during probe (no real reason for struct adp5585_info) 
> > 
> > Anyways, If you prefer the above I'm not going to argue against it...
> > 
> > > 
> > > > +};
> > > > +
> > > > +static struct adp5585_info adp5585_02_info = {
> > > > +	.regmap_type = ADP5585_REGMAP_02,
> > > > +};
> > > > +
> > > > +static struct adp5585_info adp5585_04_info = {
> > > > +	.regmap_type = ADP5585_REGMAP_04,
> > > > +};
> > > > +
> > > >  static const struct of_device_id adp5585_of_match[] = {
> > > >  	{
> > > >  		.compatible = "adi,adp5585-00",
> > > > -		.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > > +		.data = &adp5585_info,
> > > 
> > > 		.data = ADP5585_REGMAP_00,
> > 
> > I see, needs a cast but should work. I personally prefer valid pointers than
> > "encoding" integers in here. I know we can start the enum at 1 so that we can
> > still look for 0 for any possible issue but...
> 
> I also prefer pointers to structures, but won't fight for it.

They can be nice to avoid any switch() matching, but here we have both
anyway so it improves very little.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver
  2025-05-13 15:39     ` Nuno Sá
@ 2025-05-13 16:04       ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16:04 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Laurent Pinchart, 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, Liu Ying

On Tue, 13 May 2025, Nuno Sá wrote:

> On Tue, 2025-05-13 at 17:26 +0200, Laurent Pinchart wrote:
> > Hi Nuno,
> > 
> > Thank you for the patch.
> > 
> > On Mon, May 12, 2025 at 01:38:56PM +0100, Nuno Sá via B4 Relay wrote:
> > > From: Nuno Sá <nuno.sa@analog.com>
> > > 
> > > The adp5585 is a Multi Function Device that can also be a gpio
> > > controller and as it turns out, when OSC_EN is not set, we can't
> > > reliably read the gpio value when it's configured as input. Hence,
> > > OSC_EN will be set during probe by the parent device (and cleared on
> > > unbind).
> > > 
> > > Moreover, we'll add support for the keymap matrix (input device) which
> > > definitely needs OSC_EN to be set and so, we cannot afford that disabling
> > > the PWM output also breaks the keymap events generation.
> > 
> > I think you can squash this with 03/22 if you send a new version. Moving
> > the OSC_EN bit handling from the PWM child driver to the MFD driver is a
> > single logical change.
> 
> Will do... happy to reduce the number of patches in the series

+1

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe
  2025-05-13 14:52     ` Nuno Sá
@ 2025-05-13 16:07       ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16: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 Tue, 13 May 2025, Nuno Sá wrote:
> On Tue, 2025-05-13 at 15:26 +0100, Lee Jones wrote:
> > On Mon, 12 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).
> > > 
> > > 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
> > > 02f9e8c1c6a1d8b9516c060e0024d69886e9fb7a..d693b1ead05128e02f671ca465f9c72cab
> > > 3b3395 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -143,6 +143,13 @@ static int adp5585_parse_fw(struct device *dev, struct
> > > adp5585_dev *adp5585,
> > >  	return rc;
> > >  }
> > >  
> > > +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;
> > > @@ -176,6 +183,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > >  	if (n_devs < 0)
> > >  		return n_devs;
> > >  
> > > +	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > > +			      ADP5585_OSC_EN);
> > 
> > Nit: Consider unwrapping to 100-chars to avoid these simple line breaks.
> > 
> > Other than that, looks okay.
> 
> This topic is always hard as some other maintainers perfect the rule "keep the
> 80 char and only go 100 if readability is hurt). Personally, I do prefer 100 so
> happy to do it here.

Yes, it like many things are subsystem / maintainer preference.

It's not the 1990's anymore - we have 4k monitors - they'll come around.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 02/22] mfd: adp5585: only add devices given in FW
  2025-05-13 15:19       ` Laurent Pinchart
  2025-05-13 15:37         ` Nuno Sá
@ 2025-05-13 16:12         ` Lee Jones
  1 sibling, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16:12 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Nuno Sá, 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, Liu Ying

On Tue, 13 May 2025, Laurent Pinchart wrote:

> On Tue, May 13, 2025 at 04:02:11PM +0100, Nuno Sá wrote:
> > On Tue, 2025-05-13 at 15:34 +0100, Lee Jones wrote:
> > > On Mon, 12 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 | 45 +++++++++++++++++++++++++++++++++++++++++----
> > > >  1 file changed, 41 insertions(+), 4 deletions(-)
> > > > 
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..02f9e8c1c6a1d8b9516c060e0024d69886
> > > > e9fb7a 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,12 +116,40 @@ static const struct regmap_config
> > > > adp5585_regmap_configs[] = {
> > > >  	},
> > > >  };
> > > >  
> > > > +static int adp5585_parse_fw(struct device *dev, struct adp5585_dev
> > > > *adp5585,
> > > > +			    struct mfd_cell **devs)
> > > > +{
> > > > +	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> > > > +
> > > > +	if (device_property_present(dev, "#pwm-cells"))
> > > > +		has_pwm = 1;
> > > 
> > > This is a little sloppy.  Instead of using throwaway local variables, do
> > > what you're going to do in the if statement.
> > 
> > Then I would need to realloc my device cells... But as I realized below, this is
> > indeed not needed.
> > 
> > > > +	if (device_property_present(dev, "#gpio-cells"))
> > > > +		has_gpio = 1;
> > > > +
> > > > +	if (!has_pwm && !has_gpio)
> > > > +		return -ENODEV;
> > > 
> > > Are we really dictating which child devices to register based on random
> > > DT properties?  Why not register them anyway and have them fail if the
> 
> The properties are not random.
> 
> > > information they need is not available?  Missing / incorrect properties
> > > usually get a -EINVAL.
> > 
> > Well, this was something Laurent asked for... In the previous version I was
> > registering all the devices unconditionally.
> 
> Registering them all means we'll get error messages in the kernel log
> when the corresponding drivers will probe, while nothing is actually
> wrong. That's fairly confusing for the user.
> 
> In an ideal situation we would have child nodes in DT and only register
> child devices for existing child nodes. Unfortunately the DT bindings
> were not designed that way, so we have to live with the current
> situation.
> 
> > > > +	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
> > > > +			     GFP_KERNEL);
> > > > +	if (!*devs)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	if (has_pwm)
> > > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_PWM];
> > > > +	if (has_gpio)
> > > > +		(*devs)[rc++] = adp5585_devs[ADP5585_DEV_GPIO];
> > > 
> > > Passing around pointers to pointers for allocation (and later, pointer
> > > to functions) is not the way we wish to operate.  See how all of the
> > > other MFD drivers handle selective sub-drivers.
> > 
> > Any pointer from the top of your head (example driver)? Honestly, I do not see
> > this being that bad. Pretty much is a dynamic array of struct mfd_cel but
> > anyways, no strong feelings
> 
> I don't find it that bad either. I don't think you should use
> devm_kcalloc() though, as the memory should be freed as soon as it's not
> needed anymore.
> 
> > But... I was actually being very stupid. First I did looked at an API to only
> 
> Occasionally overseeing a possible solution isn't being stupid. Or at
> least I hope it isn't, otherwise I would be very stupid too.

Yes, likewise.  Never worry about that.

In general let's try to simplify things by not using pointers to
pointers and pointers to functions.  There are usually much nicer,
cleaner and simpler solutions.

IMHO, the above is C-hackery at its best.

Let's see where v4 takes us.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 14/22] mfd: adp5585: support reset and unlock events
  2025-05-12 12:39 ` [PATCH v3 14/22] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
@ 2025-05-13 16:22   ` Lee Jones
  2025-05-14  8:35     ` Laurent Pinchart
  0 siblings, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16:22 UTC (permalink / raw)
  To: Nuno Sá via B4 Relay
  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 Mon, 12 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       | 279 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/adp5585.h |  41 +++++++
>  2 files changed, 320 insertions(+)
> 
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 5851ad30e7323bbb891878167d0786bc60ef5d90..b1227a390fe2f932ba8060b0d722f53f45ec3b4b 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -157,6 +157,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 = {
> @@ -164,8 +167,52 @@ 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, bool has_pin5)

has_pin5 (which doesn't actually mean much to me) is passed around a lot
and is only used in one place, as far as I can see.  You also have 'dev'
available here, so why not drop it everywhere and call

   if (!device_property_present(dev, "gpio-reserved-ranges"))

... here instead?

> +{
> +	if (has_pin5) {
> +		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 GPI5 */
> +		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, bool has_pin5)
> +{
> +	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_chip_configs(struct adp5585_dev *adp5585,
>  				     struct i2c_client *i2c,
>  				     struct regmap_config *regmap_config)
> @@ -180,10 +227,13 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
>  	case ADP5585_MAN_ID_VALUE:
>  		*regmap_config = adp5585_regmap_config_template;
>  		info->regs = &adp5585_regs;
> +		info->validate_event = adp5585_validate_event;

I'd take an extra if() / switch() over a driver-level pointer to a function.

>  		break;
>  	case ADP5589_MAN_ID_VALUE:
>  		*regmap_config = adp5589_regmap_config_template;
>  		info->regs = &adp5589_regs;
> +		info->validate_event = adp5589_validate_event;
> +		info->has_unlock = true;
>  		break;
>  	default:
>  		return -ENODEV;
> @@ -215,11 +265,175 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
>  	}
>  }
>  
> +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585,
> +				  const char *prop, u32 *events, u32 *n_events,
> +				  u32 max_keys, bool reset_key, bool has_pin5)
> +{

It's not clear to me what's happening in here.  Would you be kind enough
to add some commentary for readers who don't know the device?

In fact, that applies throughout the driver please.

> +	const struct adp5585_info *info = adp5585->info;
> +	struct device *dev = adp5585->dev;
> +	unsigned int ev;
> +	int ret;
> +
> +	ret = device_property_count_u32(dev, prop);
> +	if (ret < 0)
> +		return 0;
> +
> +	*n_events = ret;
> +
> +	if (!info->has_unlock && !reset_key)
> +		return dev_err_probe(dev, -EOPNOTSUPP,
> +				     "Unlock keys not supported\n");
> +
> +	if (*n_events > max_keys)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid number of keys(%u > %u) for %s\n",
> +				     *n_events, max_keys, prop);
> +
> +	ret = device_property_read_u32_array(dev, prop, events, *n_events);
> +	if (ret)
> +		return ret;
> +
> +	for (ev = 0; ev < *n_events; ev++) {
> +		/* wildcard key */
> +		if (!reset_key && events[ev] == 127)
> +			continue;
> +
> +		ret = adp5585->info->validate_event(adp5585, events[ev], has_pin5);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585, bool has_pin5)
> +{
> +	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,
> +				     has_pin5);
> +	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, bool has_pin5)
> +{
> +	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,
> +				     has_pin5);
> +	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,
> +				     has_pin5);
> +	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 int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  			    struct mfd_cell **devs)
>  {
>  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
>  	const struct mfd_cell *cells;
> +	bool has_pin5 = false;
>  	unsigned int prop_val;
>  	int ret;
>  
> @@ -232,6 +446,17 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
>  	if (!has_pwm && !has_gpio)
>  		return -ENODEV;
>  
> +	if (!device_property_present(dev, "gpio-reserved-ranges"))
> +		has_pin5 = true;
> +
> +	ret = adp5585_unlock_ev_parse(adp5585, has_pin5);
> +	if (ret)
> +		return ret;
> +
> +	ret = adp5585_reset_ev_parse(adp5585, has_pin5);
> +	if (ret)
> +		return ret;
> +
>  	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
>  	if (!ret) {
>  		switch (prop_val) {
> @@ -344,6 +569,60 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
>  	unsigned int reg_val, i;
>  	int ret;
>  
> +	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 (adp5585->nkeys_reset1)
> +			reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
> +		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;
> +	}
> +
>  	for (i = 0; i < ADP5585_EV_MAX; i++) {
>  		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
>  		if (ret)
> diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> index 218c56bed2e0304de8b81c7090386fb4e1b6c281..034b7c18af83b1e801860ed4fca1755ff59faed1 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -71,6 +71,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
> @@ -121,6 +122,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
> @@ -131,6 +139,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
> @@ -141,8 +163,13 @@
>  #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;
> +struct adp5585_dev;
>  
>  enum adp5585_regmap_type {
>  	ADP5585_REGMAP_00,
> @@ -165,12 +192,18 @@ 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_info {
>  	const struct adp5585_regs *regs;
> +	int (*validate_event)(const struct adp5585_dev *adp5585,
> +			      unsigned int ev, bool has_pin5);
>  	enum adp5585_regmap_type regmap_type;
>  	unsigned int id;
> +	bool has_unlock;
>  };
>  
>  struct adp5585_dev {
> @@ -182,6 +215,14 @@ struct adp5585_dev {
>  	struct list_head ev_handlers;
>  	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;
>  };
>  
>  static inline void adp5585_ev_handler_remove(void *data)
> 
> -- 
> 2.49.0
> 
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 21/22] mfd: adp5585: add support for a reset pin
  2025-05-12 12:39 ` [PATCH v3 21/22] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
@ 2025-05-13 16:26   ` Lee Jones
  2025-05-15  5:39     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-13 16:26 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 Mon, 12 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 88401668f30e06ac201175470eeaf6216f3121d9..0fbe1f7f2582408b2e1b99f629182ceebce73fd7 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -11,6 +11,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>
> @@ -712,6 +713,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>  {
>  	struct regmap_config regmap_config;
>  	struct adp5585_dev *adp5585;
> +	struct gpio_desc *gpio;
>  	struct mfd_cell *devs;
>  	unsigned int id;
>  	int ret, n_devs;
> @@ -730,6 +732,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

It's okay, you can say "datasheet". :)

> +	 * reasonable values that work...

What does "..." mean in this context?

> +	 */
> +	if (gpio) {
> +		fsleep(30);
> +		gpiod_set_value_cansleep(gpio, 0);
> +		fsleep(60);
> +	}
> +
>  	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_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] 63+ messages in thread

* Re: [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (21 preceding siblings ...)
  2025-05-12 12:39 ` [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
@ 2025-05-14  8:25 ` Lee Jones
  2025-05-14 11:04   ` Nuno Sá
  2025-07-02 13:34 ` Lee Jones
  23 siblings, 1 reply; 63+ messages in thread
From: Lee Jones @ 2025-05-14  8:25 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, Bartosz Golaszewski,
	Krzysztof Kozlowski

On Mon, 12 May 2025, Nuno Sá via B4 Relay wrote:

> Hi all,
> 
> Here it goes v3. There was some major refactoring in this version due to
> Lee's and Laurent's feedback. There are some splits (and some explicit
> requests) resulting in new patches being added. The biggest change is the
> effort in trying to minimize the usage of specific child device bits in
> the top level device (mainly stuff related to the keymap). I think now
> it's fairly self contained and the only thing that we really need to
> handle in the top device are the unlock and reset events as those can be
> supported through both the input and gpio devices (via gpio_keys). This
> results in a bit of more runtime complexity but well, that's life...
> 
> Another change is Lee's suggestion of making use of templates (for
> regmap and chip specific data) and fill things up at probe.
> 
> I also refactored a bit the event handling so it's more generic now.
> There were lot's of changes so odds are that I might have forgotten some
> feedback and so, my apologies in advance :).
> 
> I also dropped the tags in:
> 
> patch 16/22 ("gpio: adp5585: support gpi events") as it has some
> significant changes (replacing .init_valid_masks() with .request() and
> .free())

Please run this set through checkpatch.pl before submitting again.

Not sure if we've discussed this, but W=1 wouldn't hurt either.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 14/22] mfd: adp5585: support reset and unlock events
  2025-05-13 16:22   ` Lee Jones
@ 2025-05-14  8:35     ` Laurent Pinchart
  2025-05-14  8:46       ` Lee Jones
  2025-05-15  5:46       ` Nuno Sá
  0 siblings, 2 replies; 63+ messages in thread
From: Laurent Pinchart @ 2025-05-14  8:35 UTC (permalink / raw)
  To: Lee Jones
  Cc: Nuno Sá via B4 Relay, linux-gpio, linux-pwm, devicetree,
	linux-input, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

On Tue, May 13, 2025 at 05:22:46PM +0100, Lee Jones wrote:
> On Mon, 12 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       | 279 ++++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/adp5585.h |  41 +++++++
> >  2 files changed, 320 insertions(+)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index 5851ad30e7323bbb891878167d0786bc60ef5d90..b1227a390fe2f932ba8060b0d722f53f45ec3b4b 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -157,6 +157,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 = {
> > @@ -164,8 +167,52 @@ 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, bool has_pin5)
> 
> has_pin5 (which doesn't actually mean much to me) is passed around a lot
> and is only used in one place, as far as I can see.  You also have 'dev'
> available here, so why not drop it everywhere and call
> 
>    if (!device_property_present(dev, "gpio-reserved-ranges"))
> 
> ... here instead?

The information can be stored in struct adp5585_dev. I wouldn't call
device_property_present() here, as that's costly.

> > +{
> > +	if (has_pin5) {
> > +		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 GPI5 */
> > +		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, bool has_pin5)
> > +{
> > +	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_chip_configs(struct adp5585_dev *adp5585,
> >  				     struct i2c_client *i2c,
> >  				     struct regmap_config *regmap_config)
> > @@ -180,10 +227,13 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
> >  	case ADP5585_MAN_ID_VALUE:
> >  		*regmap_config = adp5585_regmap_config_template;
> >  		info->regs = &adp5585_regs;
> > +		info->validate_event = adp5585_validate_event;
> 
> I'd take an extra if() / switch() over a driver-level pointer to a function.

Funny how we have different tastes for this kind of things, I find the
function pointer more readable :-)

> >  		break;
> >  	case ADP5589_MAN_ID_VALUE:
> >  		*regmap_config = adp5589_regmap_config_template;
> >  		info->regs = &adp5589_regs;
> > +		info->validate_event = adp5589_validate_event;
> > +		info->has_unlock = true;
> >  		break;
> >  	default:
> >  		return -ENODEV;
> > @@ -215,11 +265,175 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
> >  	}
> >  }
> >  
> > +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585,
> > +				  const char *prop, u32 *events, u32 *n_events,
> > +				  u32 max_keys, bool reset_key, bool has_pin5)
> > +{
> 
> It's not clear to me what's happening in here.  Would you be kind enough
> to add some commentary for readers who don't know the device?
> 
> In fact, that applies throughout the driver please.
> 
> > +	const struct adp5585_info *info = adp5585->info;
> > +	struct device *dev = adp5585->dev;
> > +	unsigned int ev;
> > +	int ret;
> > +
> > +	ret = device_property_count_u32(dev, prop);
> > +	if (ret < 0)
> > +		return 0;
> > +
> > +	*n_events = ret;
> > +
> > +	if (!info->has_unlock && !reset_key)
> > +		return dev_err_probe(dev, -EOPNOTSUPP,
> > +				     "Unlock keys not supported\n");
> > +
> > +	if (*n_events > max_keys)
> > +		return dev_err_probe(dev, -EINVAL,
> > +				     "Invalid number of keys(%u > %u) for %s\n",
> > +				     *n_events, max_keys, prop);
> > +
> > +	ret = device_property_read_u32_array(dev, prop, events, *n_events);
> > +	if (ret)
> > +		return ret;
> > +
> > +	for (ev = 0; ev < *n_events; ev++) {
> > +		/* wildcard key */
> > +		if (!reset_key && events[ev] == 127)
> > +			continue;
> > +
> > +		ret = adp5585->info->validate_event(adp5585, events[ev], has_pin5);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585, bool has_pin5)
> > +{
> > +	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,
> > +				     has_pin5);
> > +	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, bool has_pin5)
> > +{
> > +	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,
> > +				     has_pin5);
> > +	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,
> > +				     has_pin5);
> > +	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 int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> >  			    struct mfd_cell **devs)
> >  {
> >  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> >  	const struct mfd_cell *cells;
> > +	bool has_pin5 = false;
> >  	unsigned int prop_val;
> >  	int ret;
> >  
> > @@ -232,6 +446,17 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> >  	if (!has_pwm && !has_gpio)
> >  		return -ENODEV;
> >  
> > +	if (!device_property_present(dev, "gpio-reserved-ranges"))
> > +		has_pin5 = true;
> > +
> > +	ret = adp5585_unlock_ev_parse(adp5585, has_pin5);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = adp5585_reset_ev_parse(adp5585, has_pin5);
> > +	if (ret)
> > +		return ret;
> > +
> >  	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
> >  	if (!ret) {
> >  		switch (prop_val) {
> > @@ -344,6 +569,60 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> >  	unsigned int reg_val, i;
> >  	int ret;
> >  
> > +	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 (adp5585->nkeys_reset1)
> > +			reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
> > +		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;
> > +	}
> > +
> >  	for (i = 0; i < ADP5585_EV_MAX; i++) {
> >  		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
> >  		if (ret)
> > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > index 218c56bed2e0304de8b81c7090386fb4e1b6c281..034b7c18af83b1e801860ed4fca1755ff59faed1 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -71,6 +71,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
> > @@ -121,6 +122,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
> > @@ -131,6 +139,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
> > @@ -141,8 +163,13 @@
> >  #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;
> > +struct adp5585_dev;
> >  
> >  enum adp5585_regmap_type {
> >  	ADP5585_REGMAP_00,
> > @@ -165,12 +192,18 @@ 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_info {
> >  	const struct adp5585_regs *regs;
> > +	int (*validate_event)(const struct adp5585_dev *adp5585,
> > +			      unsigned int ev, bool has_pin5);
> >  	enum adp5585_regmap_type regmap_type;
> >  	unsigned int id;
> > +	bool has_unlock;
> >  };
> >  
> >  struct adp5585_dev {
> > @@ -182,6 +215,14 @@ struct adp5585_dev {
> >  	struct list_head ev_handlers;
> >  	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;
> >  };
> >  
> >  static inline void adp5585_ev_handler_remove(void *data)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 14/22] mfd: adp5585: support reset and unlock events
  2025-05-14  8:35     ` Laurent Pinchart
@ 2025-05-14  8:46       ` Lee Jones
  2025-05-15  5:46       ` Nuno Sá
  1 sibling, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-05-14  8:46 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Nuno Sá via B4 Relay, linux-gpio, linux-pwm, devicetree,
	linux-input, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

On Wed, 14 May 2025, Laurent Pinchart wrote:

> On Tue, May 13, 2025 at 05:22:46PM +0100, Lee Jones wrote:
> > On Mon, 12 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       | 279 ++++++++++++++++++++++++++++++++++++++++++++
> > >  include/linux/mfd/adp5585.h |  41 +++++++
> > >  2 files changed, 320 insertions(+)
> > > 
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index 5851ad30e7323bbb891878167d0786bc60ef5d90..b1227a390fe2f932ba8060b0d722f53f45ec3b4b 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -157,6 +157,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 = {
> > > @@ -164,8 +167,52 @@ 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, bool has_pin5)
> > 
> > has_pin5 (which doesn't actually mean much to me) is passed around a lot
> > and is only used in one place, as far as I can see.  You also have 'dev'
> > available here, so why not drop it everywhere and call
> > 
> >    if (!device_property_present(dev, "gpio-reserved-ranges"))
> > 
> > ... here instead?
> 
> The information can be stored in struct adp5585_dev. I wouldn't call
> device_property_present() here, as that's costly.

Does this function get called a lot?

Storing in the device data is also good.

> > > +{
> > > +	if (has_pin5) {
> > > +		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 GPI5 */
> > > +		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, bool has_pin5)
> > > +{
> > > +	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_chip_configs(struct adp5585_dev *adp5585,
> > >  				     struct i2c_client *i2c,
> > >  				     struct regmap_config *regmap_config)
> > > @@ -180,10 +227,13 @@ static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
> > >  	case ADP5585_MAN_ID_VALUE:
> > >  		*regmap_config = adp5585_regmap_config_template;
> > >  		info->regs = &adp5585_regs;
> > > +		info->validate_event = adp5585_validate_event;
> > 
> > I'd take an extra if() / switch() over a driver-level pointer to a function.
> 
> Funny how we have different tastes for this kind of things, I find the
> function pointer more readable :-)

Contributors have tried to do "interesting" things with function
pointers in MFD in the past.  To the point were I now have a general
aversion to them.  I think they're great for things like subsystem-level
Ops, but beyond that, things _can_ get messy, fast.

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver
  2025-05-14  8:25 ` [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Lee Jones
@ 2025-05-14 11:04   ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-14 11:04 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, Bartosz Golaszewski,
	Krzysztof Kozlowski

On Wed, 2025-05-14 at 09:25 +0100, Lee Jones wrote:
> On Mon, 12 May 2025, Nuno Sá via B4 Relay wrote:
> 
> > Hi all,
> > 
> > Here it goes v3. There was some major refactoring in this version due to
> > Lee's and Laurent's feedback. There are some splits (and some explicit
> > requests) resulting in new patches being added. The biggest change is the
> > effort in trying to minimize the usage of specific child device bits in
> > the top level device (mainly stuff related to the keymap). I think now
> > it's fairly self contained and the only thing that we really need to
> > handle in the top device are the unlock and reset events as those can be
> > supported through both the input and gpio devices (via gpio_keys). This
> > results in a bit of more runtime complexity but well, that's life...
> > 
> > Another change is Lee's suggestion of making use of templates (for
> > regmap and chip specific data) and fill things up at probe.
> > 
> > I also refactored a bit the event handling so it's more generic now.
> > There were lot's of changes so odds are that I might have forgotten some
> > feedback and so, my apologies in advance :).
> > 
> > I also dropped the tags in:
> > 
> > patch 16/22 ("gpio: adp5585: support gpi events") as it has some
> > significant changes (replacing .init_valid_masks() with .request() and
> > .free())
> 
> Please run this set through checkpatch.pl before submitting again.
> 

I've done that... It gave some issues but not sure there's anything to be done:

● 26ffbc19b2ce: mfd: adp5585: refactor how regmap defaults are handled
  ● checkpatch.pl: drivers/mfd/adp5585.c:94: WARNING: struct regmap_config
should normally be const
  ● checkpatch.pl: drivers/mfd/adp5585.c:149: WARNING: struct regmap_config
should normally be const

The above is something we need given that we want to fill reg defaults during
probe. Maybe we can go around that but not sure if it's worth it.

● 6183fc9ef938: gpio: adp5585: add support for the adp5589 expander
  ● checkpatch.pl: drivers/gpio/gpio-adp5585.c:26: CHECK: Macro argument reuse
'n' - possible side-effects?

Again, not sure it's worth it to "fix" it and it was just copy pasted from the
header.

● 3e0f3ba80ea5: Input: adp5589: remove the driver
  ● checkpatch.pl: drivers/input/keyboard/adp5589-keys.c:14: WARNING: added,
moved or deleted file(s), does MAINTAINERS need updating?

the above is lack of an entry in the original file...



> Not sure if we've discussed this, but W=1 wouldn't hurt either.

I used the kernel test bot... I assume W=1 is part of the builds.

- Nuno Sá

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 21/22] mfd: adp5585: add support for a reset pin
  2025-05-13 16:26   ` Lee Jones
@ 2025-05-15  5:39     ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-15  5:39 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 Tue, 2025-05-13 at 17:26 +0100, Lee Jones wrote:
> On Mon, 12 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
> > 88401668f30e06ac201175470eeaf6216f3121d9..0fbe1f7f2582408b2e1b99f629182ceebce73fd
> > 7 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -11,6 +11,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>
> > @@ -712,6 +713,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  {
> >  	struct regmap_config regmap_config;
> >  	struct adp5585_dev *adp5585;
> > +	struct gpio_desc *gpio;
> >  	struct mfd_cell *devs;
> >  	unsigned int id;
> >  	int ret, n_devs;
> > @@ -730,6 +732,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
> 
> It's okay, you can say "datasheet". :)
> 
> > +	 * reasonable values that work...
> 
> What does "..." mean in this context?

Nothing :). Just an annoying habit I have. It comes automatically

> 
> > +	 */
> > +	if (gpio) {
> > +		fsleep(30);
> > +		gpiod_set_value_cansleep(gpio, 0);
> > +		fsleep(60);
> > +	}
> > +
> >  	adp5585->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
> >  	if (IS_ERR(adp5585->regmap))
> >  		return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > 
> > -- 
> > 2.49.0
> > 
> > 
> 


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 14/22] mfd: adp5585: support reset and unlock events
  2025-05-14  8:35     ` Laurent Pinchart
  2025-05-14  8:46       ` Lee Jones
@ 2025-05-15  5:46       ` Nuno Sá
  1 sibling, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-15  5:46 UTC (permalink / raw)
  To: Laurent Pinchart, Lee Jones
  Cc: Nuno Sá via B4 Relay, linux-gpio, linux-pwm, devicetree,
	linux-input, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Liu Ying

On Wed, 2025-05-14 at 10:35 +0200, Laurent Pinchart wrote:
> On Tue, May 13, 2025 at 05:22:46PM +0100, Lee Jones wrote:
> > On Mon, 12 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       | 279 ++++++++++++++++++++++++++++++++++++++++++++
> > >  include/linux/mfd/adp5585.h |  41 +++++++
> > >  2 files changed, 320 insertions(+)
> > > 
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 5851ad30e7323bbb891878167d0786bc60ef5d90..b1227a390fe2f932ba8060b0d722f53f45ec3
> > > b4b 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -157,6 +157,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 = {
> > > @@ -164,8 +167,52 @@ 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, bool has_pin5)
> > 
> > has_pin5 (which doesn't actually mean much to me) is passed around a lot
> > and is only used in one place, as far as I can see.  You also have 'dev'
> > available here, so why not drop it everywhere and call
> > 
> >    if (!device_property_present(dev, "gpio-reserved-ranges"))
> > 
> > ... here instead?
> 
> The information can be stored in struct adp5585_dev. I wouldn't call
> device_property_present() here, as that's costly.

That's the plan for v4. TBH, I was never happy with attaching the pin5/row5
information with that property (I think something explicit in chip data structure to
be better) but this was a sneaky way of avoiding another chip_info varriable for the
the variant with this pin available.

But now that we'll be adding enums, It's easier.

> 
> > > +{
> > > +	if (has_pin5) {
> > > +		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 GPI5 */
> > > +		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, bool has_pin5)
> > > +{
> > > +	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_chip_configs(struct adp5585_dev *adp5585,
> > >  				     struct i2c_client *i2c,
> > >  				     struct regmap_config *regmap_config)
> > > @@ -180,10 +227,13 @@ static int adp5585_fill_chip_configs(struct adp5585_dev
> > > *adp5585,
> > >  	case ADP5585_MAN_ID_VALUE:
> > >  		*regmap_config = adp5585_regmap_config_template;
> > >  		info->regs = &adp5585_regs;
> > > +		info->validate_event = adp5585_validate_event;
> > 
> > I'd take an extra if() / switch() over a driver-level pointer to a function.
> 
> Funny how we have different tastes for this kind of things, I find the
> function pointer more readable :-)

Yes :)

- Nuno Sá

> 
> > >  		break;
> > >  	case ADP5589_MAN_ID_VALUE:
> > >  		*regmap_config = adp5589_regmap_config_template;
> > >  		info->regs = &adp5589_regs;
> > > +		info->validate_event = adp5589_validate_event;
> > > +		info->has_unlock = true;
> > >  		break;
> > >  	default:
> > >  		return -ENODEV;
> > > @@ -215,11 +265,175 @@ static int adp5585_fill_chip_configs(struct adp5585_dev
> > > *adp5585,
> > >  	}
> > >  }
> > >  
> > > +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585,
> > > +				  const char *prop, u32 *events, u32
> > > *n_events,
> > > +				  u32 max_keys, bool reset_key, bool has_pin5)
> > > +{
> > 
> > It's not clear to me what's happening in here.  Would you be kind enough
> > to add some commentary for readers who don't know the device?
> > 
> > In fact, that applies throughout the driver please.
> > 
> > > +	const struct adp5585_info *info = adp5585->info;
> > > +	struct device *dev = adp5585->dev;
> > > +	unsigned int ev;
> > > +	int ret;
> > > +
> > > +	ret = device_property_count_u32(dev, prop);
> > > +	if (ret < 0)
> > > +		return 0;
> > > +
> > > +	*n_events = ret;
> > > +
> > > +	if (!info->has_unlock && !reset_key)
> > > +		return dev_err_probe(dev, -EOPNOTSUPP,
> > > +				     "Unlock keys not supported\n");
> > > +
> > > +	if (*n_events > max_keys)
> > > +		return dev_err_probe(dev, -EINVAL,
> > > +				     "Invalid number of keys(%u > %u) for
> > > %s\n",
> > > +				     *n_events, max_keys, prop);
> > > +
> > > +	ret = device_property_read_u32_array(dev, prop, events, *n_events);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	for (ev = 0; ev < *n_events; ev++) {
> > > +		/* wildcard key */
> > > +		if (!reset_key && events[ev] == 127)
> > > +			continue;
> > > +
> > > +		ret = adp5585->info->validate_event(adp5585, events[ev],
> > > has_pin5);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585, bool has_pin5)
> > > +{
> > > +	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,
> > > +				     has_pin5);
> > > +	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, bool has_pin5)
> > > +{
> > > +	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,
> > > +				     has_pin5);
> > > +	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,
> > > +				     has_pin5);
> > > +	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 int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
> > >  			    struct mfd_cell **devs)
> > >  {
> > >  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> > >  	const struct mfd_cell *cells;
> > > +	bool has_pin5 = false;
> > >  	unsigned int prop_val;
> > >  	int ret;
> > >  
> > > @@ -232,6 +446,17 @@ static int adp5585_parse_fw(struct device *dev, struct
> > > adp5585_dev *adp5585,
> > >  	if (!has_pwm && !has_gpio)
> > >  		return -ENODEV;
> > >  
> > > +	if (!device_property_present(dev, "gpio-reserved-ranges"))
> > > +		has_pin5 = true;
> > > +
> > > +	ret = adp5585_unlock_ev_parse(adp5585, has_pin5);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = adp5585_reset_ev_parse(adp5585, has_pin5);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > >  	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
> > >  	if (!ret) {
> > >  		switch (prop_val) {
> > > @@ -344,6 +569,60 @@ static int adp5585_setup(struct adp5585_dev *adp5585)
> > >  	unsigned int reg_val, i;
> > >  	int ret;
> > >  
> > > +	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 (adp5585->nkeys_reset1)
> > > +			reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
> > > +		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;
> > > +	}
> > > +
> > >  	for (i = 0; i < ADP5585_EV_MAX; i++) {
> > >  		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i,
> > > &reg_val);
> > >  		if (ret)
> > > diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h
> > > index
> > > 218c56bed2e0304de8b81c7090386fb4e1b6c281..034b7c18af83b1e801860ed4fca1755ff59fa
> > > ed1 100644
> > > --- a/include/linux/mfd/adp5585.h
> > > +++ b/include/linux/mfd/adp5585.h
> > > @@ -71,6 +71,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
> > > @@ -121,6 +122,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
> > > @@ -131,6 +139,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
> > > @@ -141,8 +163,13 @@
> > >  #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;
> > > +struct adp5585_dev;
> > >  
> > >  enum adp5585_regmap_type {
> > >  	ADP5585_REGMAP_00,
> > > @@ -165,12 +192,18 @@ 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_info {
> > >  	const struct adp5585_regs *regs;
> > > +	int (*validate_event)(const struct adp5585_dev *adp5585,
> > > +			      unsigned int ev, bool has_pin5);
> > >  	enum adp5585_regmap_type regmap_type;
> > >  	unsigned int id;
> > > +	bool has_unlock;
> > >  };
> > >  
> > >  struct adp5585_dev {
> > > @@ -182,6 +215,14 @@ struct adp5585_dev {
> > >  	struct list_head ev_handlers;
> > >  	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;
> > >  };
> > >  
> > >  static inline void adp5585_ev_handler_remove(void *data)
> 


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 13/22] mfd: adp5585: add support for event handling
  2025-05-13 15:59   ` Lee Jones
@ 2025-05-15  5:56     ` Nuno Sá
  2025-05-15  6:14     ` Nuno Sá
  1 sibling, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-15  5:56 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 Tue, 2025-05-13 at 16:59 +0100, Lee Jones wrote:
> On Mon, 12 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       | 180 ++++++++++++++++++++++++++++++++++++++++++--
> >  include/linux/mfd/adp5585.h |  48 ++++++++++++
> >  2 files changed, 223 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 8be7a76842f639cbe90ad0eb956a7a3eef43fa3d..5851ad30e7323bbb891878167d0786bc60ef5d9
> > 0 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -154,10 +154,16 @@ static const struct regmap_config
> > adp5585_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_chip_configs(struct adp5585_dev *adp5585,
> > @@ -214,6 +220,8 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  {
> >  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> >  	const struct mfd_cell *cells;
> > +	unsigned int prop_val;
> > +	int ret;
> >  
> >  	if (device_property_present(dev, "#pwm-cells"))
> >  		has_pwm = 1;
> > @@ -224,6 +232,25 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  	if (!has_pwm && !has_gpio)
> >  		return -ENODEV;
> >  
> > +	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
> > +	if (!ret) {
> > +		switch (prop_val) {
> > +		case 10:
> > +			fallthrough;
> > +		case 20:
> > +			fallthrough;
> > +		case 30:
> > +			fallthrough;
> > +		case 40:
> > +			adp5585->ev_poll_time = prop_val / 10 - 1;
> > +			break;
> > +		default:
> > +			return dev_err_probe(dev, -EINVAL,
> > +					     "Invalid value(%u) for poll-
> > interval\n",
> > +					     prop_val);
> > +		}
> > +	}
> 
> This all seems like a lot of code for:
> 
> 	ev_poll_time = prop_val / 10 - 1;
> 	if (ev_poll_time > 3 || ev_poll_time < 0)
> 		return dev_err_probe();

Yes. In this enum FW parsing stuff I tend to like this switch() so it's easier to
reason about the allowed values. No strong feelings though...

> 
> > +
> >  	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
> >  			     GFP_KERNEL);
> >  	if (!*devs)
> > @@ -249,6 +276,135 @@ 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)
> > +{
> > +	struct adp5585_ev_handler *h;
> > +	unsigned int i;
> > +
> > +	guard(mutex)(&adp5585->ev_lock);
> > +
> > +	if (list_empty(&adp5585->ev_handlers)) {
> > +		dev_warn_ratelimited(adp5585->dev, "No event handlers
> > registered\n");
> > +		return;
> > +	}
> > +
> > +	for (i = 0; i < ev_cnt; i++) {
> > +		unsigned int key, key_val, key_press;
> > +		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);
> > +
> > +		list_for_each_entry(h, &adp5585->ev_handlers, entry) {
> > +			ret = h->handler(h->dev, key_val, key_press);
> 
> Rather than rolling your own call-back handler mechanism.  Are you sure
> the kernel doesn't provide a generic solution for this?  For instance,
> would a shared workqueue be better?  This way you could also exit IRQ
> context sooner as well.
> 

I'll have to check it but not sure there's anything simple enough. I'm not too
convinced with the workqueue and deferring the handling to a worker.

> > +			if (!ret)
> > +				/* handled! */
> 
> All comments should start with an upper case char.
> 
> > +				break;
> > +		}
> > +	}
> > +}
> > +
> > +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");
> 
> Strange capitalisation.
> 
> > +
> > +	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);
> 
> You don't want to propagate any errors?

We should return irqreturn_t anyways... That's why I ignore it.

> 
> > +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->info->regs;
> > +	unsigned int reg_val, i;
> > +	int ret;
> 
> The final version of this function needs some nice commentary to explain
> what each step is doing.  May as well start now.
> 

ack

> > +	for (i = 0; i < ADP5585_EV_MAX; i++) {
> > +		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i,
> > &reg_val);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> > +			   adp5585->ev_poll_time);
> > +	if (ret)
> > +		return ret;
> > +
> > +	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 void adp5585_irq_disable(void *data)
> > +{
> > +	struct adp5585_dev *adp5585 = data;
> > +
> > +	regmap_write(adp5585->regmap, adp5585->info->regs->int_en, 0);
> > +}
> > +
> > +static int adp5585_irq_enable(struct i2c_client *i2c,
> > +			      struct adp5585_dev *adp5585)
> > +{
> > +	const struct adp5585_regs *regs = adp5585->info->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... */
> 
> Uppercase char (I won't report on this again) and now silly punctuation
> please...
> 
> > +	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;
> 
> What does reading status values then writing them right back do?
> 

That's how we clear outstanding interrupts. I do have the comment but I can be more
explicit :)

> Commentary throughout please.
> 
> > +	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);
> 
> Feel free to use 100-chars to help with these early line breaks.
> 
> > +}
> > +
> >  static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  {
> >  	struct regmap_config regmap_config;
> > @@ -282,16 +438,19 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  		return dev_err_probe(&i2c->dev, -ENODEV,
> >  				     "Invalid device ID 0x%02x\n", id);
> >  
> > +	adp5585->dev = &i2c->dev;
> > +	adp5585->irq = i2c->irq;
> > +	INIT_LIST_HEAD(&adp5585->ev_handlers);
> > +
> >  	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> >  	if (n_devs < 0)
> >  		return n_devs;
> >  
> > -	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > -			      ADP5585_OSC_EN);
> > +	ret = adp5585_setup(adp5585);
> >  	if (ret)
> >  		return ret;
> >  
> > -	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> > +	ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
> >  	if (ret)
> >  		return ret;
> >  
> > @@ -301,13 +460,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  		return dev_err_probe(&i2c->dev, ret,
> >  				     "Failed to add child devices\n");
> >  
> > -	return 0;
> > +	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;
> > @@ -316,11 +478,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
> > 9a925a91c772722db559c9ec8ae334b2159ede79..218c56bed2e0304de8b81c7090386fb4e1b6c28
> > 1 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -10,13 +10,23 @@
> >  #define __MFD_ADP5585_H_
> >  
> >  #include <linux/bits.h>
> > +#include <linux/cleanup.h>
> > +#include <linux/device.h>
> > +#include <linux/list.h>
> > +#include <linux/mutex.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 +42,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 +115,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 +134,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
> >  
> > @@ -138,8 +153,18 @@ enum adp5585_regmap_type {
> >  	ADP5589_REGMAP_02,
> >  };
> >  
> > +struct adp5585_ev_handler {
> > +	struct list_head entry;
> > +	struct device *dev;
> > +	int (*handler)(struct device *dev, unsigned int key_val,
> > +		       unsigned int key_press);
> 
> Pointer to functions outside of subsystem-level ops are generally
> frowned upon.  Are you sure there isn't a standard way to achieve your
> goal without them?

I'll see but nothing that comes from the top of my head. You really have an aversion
to function pointers =)

- Nuno Sá

> 
> > +};
> > +
> >  struct adp5585_regs {
> > +	unsigned int gen_cfg;
> >  	unsigned int ext_cfg;
> > +	unsigned int int_en;
> > +	unsigned int poll_ptime_cfg;
> >  };
> >  
> >  struct adp5585_info {
> > @@ -150,7 +175,30 @@ struct adp5585_info {
> >  
> >  struct adp5585_dev {
> >  	struct regmap *regmap;
> > +	struct device *dev;
> >  	const struct adp5585_info *info;
> > +	/* Used to synchronize the availability of the event handlers */
> > +	struct mutex ev_lock;
> > +	struct list_head ev_handlers;
> > +	int irq;
> > +	unsigned int ev_poll_time;
> >  };
> >  
> > +static inline void adp5585_ev_handler_remove(void *data)
> > +{
> > +	struct adp5585_ev_handler *handler = data;
> > +	struct adp5585_dev *adp5585 = dev_get_drvdata(handler->dev->parent);
> > +
> > +	guard(mutex)(&adp5585->ev_lock);
> > +	list_del(&handler->entry);
> > +}
> > +
> > +static inline int devm_adp5585_ev_handler_add(struct adp5585_dev *adp5585,
> > +					      struct adp5585_ev_handler
> > *handler)
> > +{
> > +	guard(mutex)(&adp5585->ev_lock);
> > +	list_add_tail(&handler->entry, &adp5585->ev_handlers);
> > +	return devm_add_action_or_reset(handler->dev, adp5585_ev_handler_remove,
> > +					handler);
> > +}
> >  #endif
> > 
> > -- 
> > 2.49.0
> > 
> > 
> 


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 13/22] mfd: adp5585: add support for event handling
  2025-05-13 15:59   ` Lee Jones
  2025-05-15  5:56     ` Nuno Sá
@ 2025-05-15  6:14     ` Nuno Sá
  1 sibling, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-15  6:14 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 Tue, 2025-05-13 at 16:59 +0100, Lee Jones wrote:
> On Mon, 12 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       | 180 ++++++++++++++++++++++++++++++++++++++++++--
> >  include/linux/mfd/adp5585.h |  48 ++++++++++++
> >  2 files changed, 223 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 8be7a76842f639cbe90ad0eb956a7a3eef43fa3d..5851ad30e7323bbb891878167d0786bc60ef5d9
> > 0 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -154,10 +154,16 @@ static const struct regmap_config
> > adp5585_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_chip_configs(struct adp5585_dev *adp5585,
> > @@ -214,6 +220,8 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  {
> >  	unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
> >  	const struct mfd_cell *cells;
> > +	unsigned int prop_val;
> > +	int ret;
> >  
> >  	if (device_property_present(dev, "#pwm-cells"))
> >  		has_pwm = 1;
> > @@ -224,6 +232,25 @@ static int adp5585_parse_fw(struct device *dev, struct
> > adp5585_dev *adp5585,
> >  	if (!has_pwm && !has_gpio)
> >  		return -ENODEV;
> >  
> > +	ret = device_property_read_u32(dev, "poll-interval", &prop_val);
> > +	if (!ret) {
> > +		switch (prop_val) {
> > +		case 10:
> > +			fallthrough;
> > +		case 20:
> > +			fallthrough;
> > +		case 30:
> > +			fallthrough;
> > +		case 40:
> > +			adp5585->ev_poll_time = prop_val / 10 - 1;
> > +			break;
> > +		default:
> > +			return dev_err_probe(dev, -EINVAL,
> > +					     "Invalid value(%u) for poll-
> > interval\n",
> > +					     prop_val);
> > +		}
> > +	}
> 
> This all seems like a lot of code for:
> 
> 	ev_poll_time = prop_val / 10 - 1;
> 	if (ev_poll_time > 3 || ev_poll_time < 0)
> 		return dev_err_probe();
> 
> > +
> >  	*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
> >  			     GFP_KERNEL);
> >  	if (!*devs)
> > @@ -249,6 +276,135 @@ 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)
> > +{
> > +	struct adp5585_ev_handler *h;
> > +	unsigned int i;
> > +
> > +	guard(mutex)(&adp5585->ev_lock);
> > +
> > +	if (list_empty(&adp5585->ev_handlers)) {
> > +		dev_warn_ratelimited(adp5585->dev, "No event handlers
> > registered\n");
> > +		return;
> > +	}
> > +
> > +	for (i = 0; i < ev_cnt; i++) {
> > +		unsigned int key, key_val, key_press;
> > +		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);
> > +
> > +		list_for_each_entry(h, &adp5585->ev_handlers, entry) {
> > +			ret = h->handler(h->dev, key_val, key_press);
> 
> Rather than rolling your own call-back handler mechanism.  Are you sure
> the kernel doesn't provide a generic solution for this?  For instance,
> would a shared workqueue be better?  This way you could also exit IRQ
> context sooner as well.
> 
> > +			if (!ret)
> > +				/* handled! */
> 
> All comments should start with an upper case char.
> 
> > +				break;
> > +		}
> > +	}
> > +}
> > +
> > +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");
> 
> Strange capitalisation.
> 
> > +
> > +	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);
> 
> You don't want to propagate any errors?
> 
> > +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->info->regs;
> > +	unsigned int reg_val, i;
> > +	int ret;
> 
> The final version of this function needs some nice commentary to explain
> what each step is doing.  May as well start now.
> 
> > +	for (i = 0; i < ADP5585_EV_MAX; i++) {
> > +		ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i,
> > &reg_val);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> > +			   adp5585->ev_poll_time);
> > +	if (ret)
> > +		return ret;
> > +
> > +	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 void adp5585_irq_disable(void *data)
> > +{
> > +	struct adp5585_dev *adp5585 = data;
> > +
> > +	regmap_write(adp5585->regmap, adp5585->info->regs->int_en, 0);
> > +}
> > +
> > +static int adp5585_irq_enable(struct i2c_client *i2c,
> > +			      struct adp5585_dev *adp5585)
> > +{
> > +	const struct adp5585_regs *regs = adp5585->info->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... */
> 
> Uppercase char (I won't report on this again) and now silly punctuation
> please...
> 
> > +	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;
> 
> What does reading status values then writing them right back do?
> 
> Commentary throughout please.
> 
> > +	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);
> 
> Feel free to use 100-chars to help with these early line breaks.
> 
> > +}
> > +
> >  static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  {
> >  	struct regmap_config regmap_config;
> > @@ -282,16 +438,19 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  		return dev_err_probe(&i2c->dev, -ENODEV,
> >  				     "Invalid device ID 0x%02x\n", id);
> >  
> > +	adp5585->dev = &i2c->dev;
> > +	adp5585->irq = i2c->irq;
> > +	INIT_LIST_HEAD(&adp5585->ev_handlers);
> > +
> >  	n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
> >  	if (n_devs < 0)
> >  		return n_devs;
> >  
> > -	ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > -			      ADP5585_OSC_EN);
> > +	ret = adp5585_setup(adp5585);
> >  	if (ret)
> >  		return ret;
> >  
> > -	ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> > +	ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
> >  	if (ret)
> >  		return ret;
> >  
> > @@ -301,13 +460,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >  		return dev_err_probe(&i2c->dev, ret,
> >  				     "Failed to add child devices\n");
> >  
> > -	return 0;
> > +	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;
> > @@ -316,11 +478,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
> > 9a925a91c772722db559c9ec8ae334b2159ede79..218c56bed2e0304de8b81c7090386fb4e1b6c28
> > 1 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -10,13 +10,23 @@
> >  #define __MFD_ADP5585_H_
> >  
> >  #include <linux/bits.h>
> > +#include <linux/cleanup.h>
> > +#include <linux/device.h>
> > +#include <linux/list.h>
> > +#include <linux/mutex.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 +42,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 +115,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 +134,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
> >  
> > @@ -138,8 +153,18 @@ enum adp5585_regmap_type {
> >  	ADP5589_REGMAP_02,
> >  };
> >  
> > +struct adp5585_ev_handler {
> > +	struct list_head entry;
> > +	struct device *dev;
> > +	int (*handler)(struct device *dev, unsigned int key_val,
> > +		       unsigned int key_press);
> 
> Pointer to functions outside of subsystem-level ops are generally
> frowned upon.  Are you sure there isn't a standard way to achieve your
> goal without them?

Actually, it was fast. I think the notifier API will do fine here. I guess that would
make you happier?

- Nuno Sá 
> 
> > +};
> > +
> >  struct adp5585_regs {
> > +	unsigned int gen_cfg;
> >  	unsigned int ext_cfg;
> > +	unsigned int int_en;
> > +	unsigned int poll_ptime_cfg;
> >  };
> >  
> >  struct adp5585_info {
> > @@ -150,7 +175,30 @@ struct adp5585_info {
> >  
> >  struct adp5585_dev {
> >  	struct regmap *regmap;
> > +	struct device *dev;
> >  	const struct adp5585_info *info;
> > +	/* Used to synchronize the availability of the event handlers */
> > +	struct mutex ev_lock;
> > +	struct list_head ev_handlers;
> > +	int irq;
> > +	unsigned int ev_poll_time;
> >  };
> >  
> > +static inline void adp5585_ev_handler_remove(void *data)
> > +{
> > +	struct adp5585_ev_handler *handler = data;
> > +	struct adp5585_dev *adp5585 = dev_get_drvdata(handler->dev->parent);
> > +
> > +	guard(mutex)(&adp5585->ev_lock);
> > +	list_del(&handler->entry);
> > +}
> > +
> > +static inline int devm_adp5585_ev_handler_add(struct adp5585_dev *adp5585,
> > +					      struct adp5585_ev_handler
> > *handler)
> > +{
> > +	guard(mutex)(&adp5585->ev_lock);
> > +	list_add_tail(&handler->entry, &adp5585->ev_handlers);
> > +	return devm_add_action_or_reset(handler->dev, adp5585_ev_handler_remove,
> > +					handler);
> > +}
> >  #endif
> > 
> > -- 
> > 2.49.0
> > 
> > 
> 


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h
  2025-05-12 12:39 ` [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
@ 2025-05-19 16:11   ` Uwe Kleine-König
  2025-05-19 16:19     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Uwe Kleine-König @ 2025-05-19 16:11 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Dmitry Torokhov, Laurent Pinchart, Liu Ying

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

Hello Nuno,

On Mon, May 12, 2025 at 01:39:14PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
> 
> Explicitly include mod_devicetable.h for struct platform_device_id.
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
>  drivers/pwm/pwm-adp5585.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
> index f26054c19c2e154d05780af09aee1b2431eba2eb..93d0294d048abfe1a009161025e658b58b669cd9 100644
> --- a/drivers/pwm/pwm-adp5585.c
> +++ b/drivers/pwm/pwm-adp5585.c
> @@ -20,6 +20,7 @@
>  #include <linux/mfd/adp5585.h>
>  #include <linux/minmax.h>
>  #include <linux/module.h>
> +#include <linux/mod_devicetable.h>
>  #include <linux/platform_device.h>
>  #include <linux/pwm.h>
>  #include <linux/regmap.h>

This looks relevant for the current state of the driver in mainline and
doesn't depend on other patches in the series.

I applied it to

	https://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux.git pwm/for-next

and so it should be included in the next next.

Thanks
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h
  2025-05-19 16:11   ` Uwe Kleine-König
@ 2025-05-19 16:19     ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-19 16:19 UTC (permalink / raw)
  To: Uwe Kleine-König, nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Dmitry Torokhov, Laurent Pinchart, Liu Ying

On Mon, 2025-05-19 at 18:11 +0200, Uwe Kleine-König wrote:
> Hello Nuno,
> 
> On Mon, May 12, 2025 at 01:39:14PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> > 
> > Explicitly include mod_devicetable.h for struct platform_device_id.
> > 
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> >  drivers/pwm/pwm-adp5585.c | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
> > index
> > f26054c19c2e154d05780af09aee1b2431eba2eb..93d0294d048abfe1a009161025e658b58b
> > 669cd9 100644
> > --- a/drivers/pwm/pwm-adp5585.c
> > +++ b/drivers/pwm/pwm-adp5585.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/mfd/adp5585.h>
> >  #include <linux/minmax.h>
> >  #include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/pwm.h>
> >  #include <linux/regmap.h>
> 
> This looks relevant for the current state of the driver in mainline and
> doesn't depend on other patches in the series.
> 
> I applied it to
> 
> 	
> https://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux.git pwm/for-
> next
> 
> and so it should be included in the next next.

Alright,

Good then that I first pushed the new version for the bots to test build it
(before sending it). Will drop this patch from the new version.

- Nuno Sá

> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
  2025-05-12 12:39 ` [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
@ 2025-05-19 22:29   ` Dmitry Torokhov
  2025-05-20  8:32     ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Dmitry Torokhov @ 2025-05-19 22:29 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Laurent Pinchart, Liu Ying

Hi Nuno,

On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote:
> +
> +	for (pin = 0; pin < n_pins; pin++) {
> +		if (keypad_pins[pin] >= adp5585->info->n_pins) {
> +			error = dev_err_probe(dev, -EINVAL,
> +					      "Invalid keypad pin(%u) defined\n",
> +					      keypad_pins[pin]);
> +			goto out_free_map;
> +		}
> +
> +		if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) {
> +			error = dev_err_probe(dev, -EBUSY,
> +					      "Keypad pin(%u) already used\n",
> +					      keypad_pins[pin]);
> +			goto out_free_map;

This jump looked confusing, together with devm, etc. I wonder, can you
move call to devm_add_action_or_reset() before the loop? It looks like
it should handle completely unpopulated pin map just fine... 

> +		}
> +
> +		__set_bit(keypad_pins[pin], &kpad->keypad);
> +	}
> +
> +	error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad);
> +	if (error)
> +		return error;
> +
> +	/*
> +	 * 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);
> +
> +	return adp5585_keys_check_special_events(adp5585, kpad);

	error = adp5585_keys_check_special_events(...);
	if (error)
		return error;

	return 0;

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
  2025-05-19 22:29   ` Dmitry Torokhov
@ 2025-05-20  8:32     ` Nuno Sá
  2025-05-20 18:33       ` Dmitry Torokhov
  0 siblings, 1 reply; 63+ messages in thread
From: Nuno Sá @ 2025-05-20  8:32 UTC (permalink / raw)
  To: Dmitry Torokhov, nuno.sa
  Cc: linux-gpio, linux-pwm, devicetree, linux-input, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Laurent Pinchart, Liu Ying

On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote:
> Hi Nuno,
> 
> On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote:
> > +
> > +	for (pin = 0; pin < n_pins; pin++) {
> > +		if (keypad_pins[pin] >= adp5585->info->n_pins) {
> > +			error = dev_err_probe(dev, -EINVAL,
> > +					      "Invalid keypad pin(%u)
> > defined\n",
> > +					      keypad_pins[pin]);
> > +			goto out_free_map;
> > +		}
> > +
> > +		if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage))
> > {
> > +			error = dev_err_probe(dev, -EBUSY,
> > +					      "Keypad pin(%u) already
> > used\n",
> > +					      keypad_pins[pin]);
> > +			goto out_free_map;
> 
> This jump looked confusing, together with devm, etc. I wonder, can you
> move call to devm_add_action_or_reset() before the loop? It looks like
> it should handle completely unpopulated pin map just fine... 

Seemed the logical way but I agree that what you suggest makes it more simpler.

> 
> > +		}
> > +
> > +		__set_bit(keypad_pins[pin], &kpad->keypad);
> > +	}
> > +
> > +	error = devm_add_action_or_reset(dev, adp5585_keys_pins_free,
> > kpad);
> > +	if (error)
> > +		return error;
> > +
> > +	/*
> > +	 * 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);
> > +
> > +	return adp5585_keys_check_special_events(adp5585, kpad);
> 
> 	error = adp5585_keys_check_special_events(...);
> 	if (error)
> 		return error;

Curious, any special reason for the above? Or is just personal preference?


- Nuno Sá

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
  2025-05-20  8:32     ` Nuno Sá
@ 2025-05-20 18:33       ` Dmitry Torokhov
  2025-05-21 10:06         ` Nuno Sá
  0 siblings, 1 reply; 63+ messages in thread
From: Dmitry Torokhov @ 2025-05-20 18:33 UTC (permalink / raw)
  To: Nuno Sá
  Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Laurent Pinchart, Liu Ying

On Tue, May 20, 2025 at 09:32:53AM +0100, Nuno Sá wrote:
> On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote:
> > Hi Nuno,
> > 
> > On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote:
> > > +
> > > +	for (pin = 0; pin < n_pins; pin++) {
> > > +		if (keypad_pins[pin] >= adp5585->info->n_pins) {
> > > +			error = dev_err_probe(dev, -EINVAL,
> > > +					      "Invalid keypad pin(%u)
> > > defined\n",
> > > +					      keypad_pins[pin]);
> > > +			goto out_free_map;
> > > +		}
> > > +
> > > +		if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage))
> > > {
> > > +			error = dev_err_probe(dev, -EBUSY,
> > > +					      "Keypad pin(%u) already
> > > used\n",
> > > +					      keypad_pins[pin]);
> > > +			goto out_free_map;
> > 
> > This jump looked confusing, together with devm, etc. I wonder, can you
> > move call to devm_add_action_or_reset() before the loop? It looks like
> > it should handle completely unpopulated pin map just fine... 
> 
> Seemed the logical way but I agree that what you suggest makes it more simpler.
> 
> > 
> > > +		}
> > > +
> > > +		__set_bit(keypad_pins[pin], &kpad->keypad);
> > > +	}
> > > +
> > > +	error = devm_add_action_or_reset(dev, adp5585_keys_pins_free,
> > > kpad);
> > > +	if (error)
> > > +		return error;
> > > +
> > > +	/*
> > > +	 * 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);
> > > +
> > > +	return adp5585_keys_check_special_events(adp5585, kpad);
> > 
> > 	error = adp5585_keys_check_special_events(...);
> > 	if (error)
> > 		return error;
> 
> Curious, any special reason for the above? Or is just personal preference?

More of a personal preference, however there is some logic to it ;) - 
in a function with multiple failure/return points such form allows for
easy addition and/or movement of the code.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
  2025-05-20 18:33       ` Dmitry Torokhov
@ 2025-05-21 10:06         ` Nuno Sá
  0 siblings, 0 replies; 63+ messages in thread
From: Nuno Sá @ 2025-05-21 10:06 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
	Laurent Pinchart, Liu Ying

On Tue, 2025-05-20 at 11:33 -0700, Dmitry Torokhov wrote:
> On Tue, May 20, 2025 at 09:32:53AM +0100, Nuno Sá wrote:
> > On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote:
> > > Hi Nuno,
> > > 
> > > On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote:
> > > > +
> > > > +	for (pin = 0; pin < n_pins; pin++) {
> > > > +		if (keypad_pins[pin] >= adp5585->info->n_pins) {
> > > > +			error = dev_err_probe(dev, -EINVAL,
> > > > +					      "Invalid keypad pin(%u)
> > > > defined\n",
> > > > +					      keypad_pins[pin]);
> > > > +			goto out_free_map;
> > > > +		}
> > > > +
> > > > +		if (test_and_set_bit(keypad_pins[pin], adp5585-
> > > > >pin_usage))
> > > > {
> > > > +			error = dev_err_probe(dev, -EBUSY,
> > > > +					      "Keypad pin(%u) already
> > > > used\n",
> > > > +					      keypad_pins[pin]);
> > > > +			goto out_free_map;
> > > 
> > > This jump looked confusing, together with devm, etc. I wonder, can you
> > > move call to devm_add_action_or_reset() before the loop? It looks like
> > > it should handle completely unpopulated pin map just fine... 
> > 
> > Seemed the logical way but I agree that what you suggest makes it more
> > simpler.
> > 
> > > 
> > > > +		}
> > > > +
> > > > +		__set_bit(keypad_pins[pin], &kpad->keypad);
> > > > +	}
> > > > +
> > > > +	error = devm_add_action_or_reset(dev, adp5585_keys_pins_free,
> > > > kpad);
> > > > +	if (error)
> > > > +		return error;
> > > > +
> > > > +	/*
> > > > +	 * 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);
> > > > +
> > > > +	return adp5585_keys_check_special_events(adp5585, kpad);
> > > 
> > > 	error = adp5585_keys_check_special_events(...);
> > > 	if (error)
> > > 		return error;
> > 
> > Curious, any special reason for the above? Or is just personal preference?
> 
> More of a personal preference, however there is some logic to it ;) - 
> in a function with multiple failure/return points such form allows for
> easy addition and/or movement of the code.
> 
> Thanks.

I get your point. Honestly still prefer returning right away but no strong
feelings anyways. I'll change it.

- Nuno Sá

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver
  2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
                   ` (22 preceding siblings ...)
  2025-05-14  8:25 ` [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Lee Jones
@ 2025-07-02 13:34 ` Lee Jones
  23 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2025-07-02 13:34 UTC (permalink / raw)
  To: linux-gpio, linux-pwm, devicetree, linux-input, Nuno Sá
  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,
	Krzysztof Kozlowski

On Mon, 12 May 2025 13:38:52 +0100, Nuno Sá wrote:
> Here it goes v3. There was some major refactoring in this version due to
> Lee's and Laurent's feedback. There are some splits (and some explicit
> requests) resulting in new patches being added. The biggest change is the
> effort in trying to minimize the usage of specific child device bits in
> the top level device (mainly stuff related to the keymap). I think now
> it's fairly self contained and the only thing that we really need to
> handle in the top device are the unlock and reset events as those can be
> supported through both the input and gpio devices (via gpio_keys). This
> results in a bit of more runtime complexity but well, that's life...
> 
> [...]

Applied, thanks!

[01/22] dt-bindings: mfd: adp5585: ease on the required properties
        commit: 09d55a54b466d60a71573c78a99a901410ef73e0
[02/22] mfd: adp5585: only add devices given in FW
        (no commit info)
[03/22] mfd: adp5585: enable oscilator during probe
        (no commit info)
[04/22] pwm: adp5585: don't control OSC_EN in the pwm driver
        (no commit info)
[05/22] mfd: adp5585: make use of MFD_CELL_NAME()
        (no commit info)
[06/22] dt-bindings: mfd: adp5585: document adp5589 I/O expander
        commit: e65e2b0d0f7e75c40f426e0f3e0a1bb6faff93e6
[07/22] mfd: adp5585: refactor how regmap defaults are handled
        (no commit info)
[08/22] mfd: adp5585: add support for adp5589
        (no commit info)
[09/22] mfd: adp5585: add a per chip reg struture
        (no commit info)
[10/22] gpio: adp5585: add support for the adp5589 expander
        commit: 9f425bf713b511b1078e0fea5a88c497e13dbb64
[11/22] pwm: adp5585: add support for adp5589
        commit: 75024f97e82e63d02b0743500efb1e264a1c2dd4
[12/22] dt-bindings: mfd: adp5585: add properties for input events
        commit: adf4932bc97ec9363dc5c0f8390ee5caccf0f41b
[13/22] mfd: adp5585: add support for event handling
        (no commit info)
[14/22] mfd: adp5585: support reset and unlock events
        (no commit info)
[15/22] mfd: adp5585: add support for input devices
        (no commit info)
[16/22] gpio: adp5585: support gpi events
        commit: 988b28a83b658137e58123f4dafc3a1588e1cb2b
[17/22] Input: adp5585: Add Analog Devices ADP5585/89 support
        commit: 19298ac01306e564b48df9aa239731cf967298d2
[18/22] Input: adp5589: remove the driver
        commit: 3bdbd0858df6574b71cacaac073f117d65a36dc6
[19/22] mfd: adp5585: support getting vdd regulator
        (no commit info)
[20/22] dt-bindings: mfd: adp5585: document reset gpio
        commit: ce262d6d629a926c8c9a2075af3b9a270ab6c641
[21/22] mfd: adp5585: add support for a reset pin
        (no commit info)
[22/22] pwm: adp5585: make sure to include mod_devicetable.h
        (no commit info)

--
Lee Jones [李琼斯]


^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2025-07-02 13:35 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-12 12:38 [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-05-12 12:38 ` [PATCH v3 01/22] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
2025-05-12 12:38 ` [PATCH v3 02/22] mfd: adp5585: only add devices given in FW Nuno Sá via B4 Relay
2025-05-13 14:34   ` Lee Jones
2025-05-13 15:02     ` Nuno Sá
2025-05-13 15:19       ` Laurent Pinchart
2025-05-13 15:37         ` Nuno Sá
2025-05-13 16:12         ` Lee Jones
2025-05-12 12:38 ` [PATCH v3 03/22] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
2025-05-13 14:26   ` Lee Jones
2025-05-13 14:52     ` Nuno Sá
2025-05-13 16:07       ` Lee Jones
2025-05-13 15:24   ` Laurent Pinchart
2025-05-13 15:38     ` Nuno Sá
2025-05-12 12:38 ` [PATCH v3 04/22] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
2025-05-13 15:26   ` Laurent Pinchart
2025-05-13 15:39     ` Nuno Sá
2025-05-13 16:04       ` Lee Jones
2025-05-12 12:38 ` [PATCH v3 05/22] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
2025-05-13 14:39   ` Lee Jones
2025-05-13 14:50     ` Nuno Sá
2025-05-12 12:38 ` [PATCH v3 06/22] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
2025-05-12 12:38 ` [PATCH v3 07/22] mfd: adp5585: refactor how regmap defaults are handled Nuno Sá via B4 Relay
2025-05-13 15:00   ` Lee Jones
2025-05-13 15:02     ` Lee Jones
2025-05-13 15:32     ` Nuno Sá
2025-05-13 15:36       ` Laurent Pinchart
2025-05-13 16:03         ` Lee Jones
2025-05-13 15:35     ` Laurent Pinchart
2025-05-13 15:41       ` Nuno Sá
2025-05-12 12:39 ` [PATCH v3 08/22] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 09/22] mfd: adp5585: add a per chip reg struture Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 10/22] gpio: adp5585: add support for the adp5589 expander Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 11/22] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 12/22] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 13/22] mfd: adp5585: add support for event handling Nuno Sá via B4 Relay
2025-05-13 15:59   ` Lee Jones
2025-05-15  5:56     ` Nuno Sá
2025-05-15  6:14     ` Nuno Sá
2025-05-12 12:39 ` [PATCH v3 14/22] mfd: adp5585: support reset and unlock events Nuno Sá via B4 Relay
2025-05-13 16:22   ` Lee Jones
2025-05-14  8:35     ` Laurent Pinchart
2025-05-14  8:46       ` Lee Jones
2025-05-15  5:46       ` Nuno Sá
2025-05-12 12:39 ` [PATCH v3 15/22] mfd: adp5585: add support for input devices Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 16/22] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 17/22] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
2025-05-19 22:29   ` Dmitry Torokhov
2025-05-20  8:32     ` Nuno Sá
2025-05-20 18:33       ` Dmitry Torokhov
2025-05-21 10:06         ` Nuno Sá
2025-05-12 12:39 ` [PATCH v3 18/22] Input: adp5589: remove the driver Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 19/22] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 20/22] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
2025-05-12 12:39 ` [PATCH v3 21/22] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
2025-05-13 16:26   ` Lee Jones
2025-05-15  5:39     ` Nuno Sá
2025-05-12 12:39 ` [PATCH v3 22/22] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
2025-05-19 16:11   ` Uwe Kleine-König
2025-05-19 16:19     ` Nuno Sá
2025-05-14  8:25 ` [PATCH v3 00/22] mfd: adp5585: support keymap events and drop legacy Input driver Lee Jones
2025-05-14 11:04   ` Nuno Sá
2025-07-02 13:34 ` Lee Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).