* [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
@ 2025-04-15 14:49 Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
` (19 more replies)
0 siblings, 20 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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
The adp5585 MFD driver was introduced in 6.11 adding support for gpio
and PWM. However, the gpio part of it was already supported as part of
the keyboard driver:
https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
On top of that it also overlapped with my refactoring of the above driver [1]
to drop usage of platform data and use FW properties instead.
Now, it actually makes sense for this device to be supported under MFD
and since the "legacy" input device depends on platform data that is not
defined anywhere the plan in this series is to add support for the
keyboard and adp5589 devices as part of the MFD driver. Once the MFD
driver supports all that's supported in the Input one, we drop it...
For DT Maintainers:
The compatible for adp5589 is part of trivial devices. To me, it makes
sense to remove it in the patch where we drop the driver but doing so
would result in a warning when adding the same compatible for the MFD
bindings. Hence, I remove it in that patch. Is that ok?
Uwe:
In my eval board, I could see that reading the GPIO value (when
configured as input) does not work when OSC_EN is not set. Therefore,
commit ("pwm: adp5585: don't control OSC_EN in the pwm driver") could
very well have a Fixes tag. However I'm not 100% sure it's a real issue
or something special to my eval board.
It would be nice if Laurent or Liu could test the PWM bits or even
check that the above is also an issue for their platform.
[1]: https://lore.kernel.org/linux-input/d1395bd61ce58b3734121bca4e09605a3e997af3.camel@gmail.com/
BTW the series is based on linux-next/master
---
Changes in v2:
- Patch 5:
* Do not nest if:then:else::if:then.
- Patch 6:
* Make use of the adp5585 info variables and adp5589 volatile regs.
- Patch 9:
* Use standard "poll-interval" property (and move it before vendor
properties).
- Patch 10:
* Make sure to include bitfield.h.
- Link to v1: https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com
---
Nuno Sá (17):
dt-bindings: mfd: adp5585: ease on the required properties
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: add support for adp5589
gpio: adp5585: add support for the ad5589 expander
pwm: adp5585: add support for adp5589
dt-bindings: mfd: adp5585: add properties for input events
mfd: adp5585: add support for key events
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 | 299 +++++-
drivers/input/keyboard/Kconfig | 21 +-
drivers/input/keyboard/Makefile | 2 +-
drivers/input/keyboard/adp5585-keys.c | 221 ++++
drivers/input/keyboard/adp5589-keys.c | 1066 --------------------
drivers/mfd/adp5585.c | 808 ++++++++++++++-
drivers/pwm/pwm-adp5585.c | 57 +-
include/linux/mfd/adp5585.h | 153 ++-
12 files changed, 1709 insertions(+), 1162 deletions(-)
---
base-commit: 5b37f7bfff3b1582c34be8fb23968b226db71ebd
change-id: 20250311-dev-adp5589-fw-e04cfd945286
--
Thanks!
- Nuno Sá
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 8:56 ` Laurent Pinchart
2025-04-21 18:57 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
` (18 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
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] 81+ messages in thread
* [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 8:57 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 03/17] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
` (17 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 160e0b38106a6d78f7d4b7c866cb603d96ea673e..f17b5f2474cac6a403556694066f438288264a49 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -110,6 +110,13 @@ static const struct regmap_config adp5585_regmap_configs[] = {
},
};
+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;
@@ -138,6 +145,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
+ 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,
adp5585_devs, ARRAY_SIZE(adp5585_devs),
NULL, 0, NULL);
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
` (16 subsequent siblings)
19 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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] 81+ messages in thread
* [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME()
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (2 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 03/17] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:03 ` Laurent Pinchart
2025-04-24 15:55 ` Lee Jones
2025-04-15 14:49 ` [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
` (15 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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...
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index f17b5f2474cac6a403556694066f438288264a49..fafe3ad93ea196e1eb8e79fecba58f36f12167eb 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>
@@ -18,8 +19,10 @@
#include <linux/types.h>
static const struct mfd_cell adp5585_devs[] = {
- { .name = "adp5585-gpio", },
- { .name = "adp5585-pwm", },
+ MFD_CELL_NAME("adp5585-keys"),
+ 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] 81+ messages in thread
* [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (3 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:07 ` Laurent Pinchart
2025-04-21 18:59 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 06/17] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
` (14 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
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..d690a411b3a4307f6dd5f4a432a357e89fefabb0 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-03
+ - adi,adp5585-04
+ - adi,adp5585-02
+ 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] 81+ messages in thread
* [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (4 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:15 ` Laurent Pinchart
2025-04-24 16:18 ` Lee Jones
2025-04-15 14:49 ` [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander Nuno Sá via B4 Relay
` (13 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
This patch adds the foundation to add support for the adp5589 gpio and pwm
drivers. Most importantly, we need to differentiate between some
registers addresses. It also hints to future keymap support.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
include/linux/mfd/adp5585.h | 57 ++++++++++-
2 files changed, 268 insertions(+), 12 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d94667993410610be7fc3672 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
};
+static const struct mfd_cell adp5589_devs[] = {
+ MFD_CELL_NAME("adp5589-keys"),
+ 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),
};
@@ -34,6 +41,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:
@@ -77,10 +93,52 @@ 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,
+};
+
enum adp5585_regmap_type {
ADP5585_REGMAP_00,
ADP5585_REGMAP_02,
ADP5585_REGMAP_04,
+ ADP5589_REGMAP_00,
+ ADP5589_REGMAP_01,
+ ADP5589_REGMAP_02,
};
static const struct regmap_config adp5585_regmap_configs[] = {
@@ -111,6 +169,131 @@ static const struct regmap_config adp5585_regmap_configs[] = {
.reg_defaults_raw = adp5585_regmap_defaults_04,
.num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
},
+ [ADP5589_REGMAP_00] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5589_MAX_REG,
+ .volatile_table = &adp5589_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults_raw = adp5589_regmap_defaults_00,
+ .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_00),
+ },
+ [ADP5589_REGMAP_01] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5589_MAX_REG,
+ .volatile_table = &adp5589_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults_raw = adp5589_regmap_defaults_01,
+ .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_01),
+ },
+ [ADP5589_REGMAP_02] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5589_MAX_REG,
+ .volatile_table = &adp5589_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults_raw = adp5589_regmap_defaults_02,
+ .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_02),
+ },
+};
+
+static const struct adp5585_regs adp5585_regs = {
+ .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,
+ .pwm_cfg = ADP5585_PWM_CFG,
+ .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5585_PWM_ONT_LOW,
+ .gen_cfg = ADP5585_GENERAL_CFG,
+ .ext_cfg = ADP5585_PIN_CONFIG_C,
+};
+
+static const struct adp5585_regs adp5589_regs = {
+ .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,
+ .pwm_cfg = ADP5589_PWM_CFG,
+ .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5589_PWM_ONT_LOW,
+ .gen_cfg = ADP5589_GENERAL_CFG,
+ .ext_cfg = ADP5589_PIN_CONFIG_D,
+};
+
+static const struct adp5585_info adp5585_info = {
+ .adp5585_devs = adp5585_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .n_devs = ARRAY_SIZE(adp5585_devs),
+ .id = ADP5585_MAN_ID_VALUE,
+ .regs = &adp5585_regs,
+ .max_rows = ADP5585_MAX_ROW_NUM,
+ .max_cols = ADP5585_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5585_01_info = {
+ .adp5585_devs = adp5585_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .n_devs = ARRAY_SIZE(adp5585_devs),
+ .id = ADP5585_MAN_ID_VALUE,
+ .regs = &adp5585_regs,
+ .max_rows = ADP5585_MAX_ROW_NUM,
+ .max_cols = ADP5585_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5585_02_info = {
+ .adp5585_devs = adp5585_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_02],
+ .n_devs = ARRAY_SIZE(adp5585_devs),
+ .id = ADP5585_MAN_ID_VALUE,
+ .regs = &adp5585_regs,
+ .max_rows = ADP5585_MAX_ROW_NUM,
+ .max_cols = ADP5585_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5585_04_info = {
+ .adp5585_devs = adp5585_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_04],
+ .n_devs = ARRAY_SIZE(adp5585_devs),
+ .id = ADP5585_MAN_ID_VALUE,
+ .regs = &adp5585_regs,
+ .max_rows = ADP5585_MAX_ROW_NUM,
+ .max_cols = ADP5585_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5589_info = {
+ .adp5585_devs = adp5589_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
+ .n_devs = ARRAY_SIZE(adp5589_devs),
+ .id = ADP5589_MAN_ID_VALUE,
+ .regs = &adp5589_regs,
+ .max_rows = ADP5589_MAX_ROW_NUM,
+ .max_cols = ADP5589_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5589_01_info = {
+ .adp5585_devs = adp5589_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
+ .n_devs = ARRAY_SIZE(adp5589_devs),
+ .id = ADP5589_MAN_ID_VALUE,
+ .regs = &adp5589_regs,
+ .max_rows = ADP5589_MAX_ROW_NUM,
+ .max_cols = ADP5589_MAX_COL_NUM,
+};
+
+static const struct adp5585_info adp5589_02_info = {
+ .adp5585_devs = adp5589_devs,
+ .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
+ .n_devs = ARRAY_SIZE(adp5589_devs),
+ .id = ADP5589_MAN_ID_VALUE,
+ .regs = &adp5589_regs,
+ .max_rows = ADP5589_MAX_ROW_NUM,
+ .max_cols = ADP5589_MAX_COL_NUM,
};
static void adp5585_osc_disable(void *data)
@@ -122,7 +305,7 @@ static void adp5585_osc_disable(void *data)
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
- const struct regmap_config *regmap_config;
+ const struct adp5585_info *info;
struct adp5585_dev *adp5585;
unsigned int id;
int ret;
@@ -133,8 +316,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, adp5585);
- regmap_config = i2c_get_match_data(i2c);
- adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ info = i2c_get_match_data(i2c);
+ if (!info)
+ return -ENODEV;
+
+ adp5585->info = info;
+
+ adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
if (IS_ERR(adp5585->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
"Failed to initialize register map\n");
@@ -144,7 +332,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);
@@ -158,8 +347,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return ret;
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
- adp5585_devs, ARRAY_SIZE(adp5585_devs),
- NULL, 0, NULL);
+ adp5585->info->adp5585_devs,
+ adp5585->info->n_devs, NULL, 0, NULL);
if (ret)
return dev_err_probe(&i2c->dev, ret,
"Failed to add child devices\n");
@@ -191,19 +380,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
static const struct of_device_id adp5585_of_match[] = {
{
.compatible = "adi,adp5585-00",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .data = &adp5585_info,
}, {
.compatible = "adi,adp5585-01",
- .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
+ .data = &adp5585_01_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,
+ }, {
+ .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 016033cd68e46757aca86d21dd37025fd354b801..dffe1449de01dacf8fe78cf0e87d1f176d11f620 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -104,9 +104,11 @@
#define ADP5585_INT_CFG BIT(1)
#define ADP5585_RST_CFG BIT(0)
#define ADP5585_INT_EN 0x3c
-
#define ADP5585_MAX_REG ADP5585_INT_EN
+#define ADP5585_MAX_ROW_NUM 6
+#define ADP5585_MAX_COL_NUM 5
+
/*
* 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
@@ -117,10 +119,63 @@
#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_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_PWM_OFFT_LOW 0x3e
+#define ADP5589_PWM_ONT_LOW 0x40
+#define ADP5589_PWM_CFG 0x42
+#define ADP5589_PIN_CONFIG_D 0x4C
+#define ADP5589_GENERAL_CFG 0x4d
+#define ADP5589_INT_EN 0x4e
+#define ADP5589_MAX_REG ADP5589_INT_EN
+
+#define ADP5589_MAX_ROW_NUM 8
+#define ADP5589_MAX_COL_NUM 11
+
+/*
+ * 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_regs {
+ 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 pwm_cfg;
+ unsigned int pwm_offt_low;
+ unsigned int pwm_ont_low;
+ unsigned int gen_cfg;
+ unsigned int ext_cfg;
+};
+
+struct adp5585_info {
+ const struct mfd_cell *adp5585_devs;
+ const struct regmap_config *regmap_config;
+ const struct adp5585_regs *regs;
+ unsigned int n_devs;
+ unsigned int id;
+ u8 max_rows;
+ u8 max_cols;
+};
+
struct regmap;
struct adp5585_dev {
struct regmap *regmap;
+ const struct adp5585_info *info;
};
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (5 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 06/17] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-17 12:27 ` Bartosz Golaszewski
2025-04-21 9:23 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 08/17] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
` (12 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/gpio/gpio-adp5585.c | 121 ++++++++++++++++++++++++++++++++------------
1 file changed, 88 insertions(+), 33 deletions(-)
diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
index d5c0f1b267c82a5002b50cbb7a108166439e4785..d8f8d5513d7f6a9acf5bdecccacc89c4615ce237 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,21 +15,49 @@
#include <linux/regmap.h>
#include <linux/types.h>
-#define ADP5585_GPIO_MAX 11
+struct adp5585_gpio_chip {
+ unsigned int max_gpio;
+ int (*bank)(unsigned int off);
+ int (*bit)(unsigned int off);
+ bool has_bias_hole;
+};
struct adp5585_gpio_dev {
struct gpio_chip gpio_chip;
+ const struct adp5585_gpio_chip *info;
struct regmap *regmap;
+ const struct adp5585_regs *regs;
};
+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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->info->bit(off);
unsigned int val;
- regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
+ regmap_read(adp5585_gpio->regmap, regs->gpio_dir_a + bank, &val);
return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
@@ -36,35 +65,37 @@ static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->info->bit(off);
- return regmap_clear_bits(adp5585_gpio->regmap,
- ADP5585_GPIO_DIRECTION_A + bank, bit);
+ return regmap_clear_bits(adp5585_gpio->regmap, regs->gpio_dir_a + bank,
+ bit);
}
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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->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, regs->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, regs->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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->info->bit(off);
unsigned int reg;
unsigned int val;
@@ -79,8 +110,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, regs->gpio_dir_a + bank, &val);
+ reg = val & bit ? regs->gpo_data_a : regs->gpi_stat_a;
regmap_read(adp5585_gpio->regmap, reg + bank, &val);
return !!(val & bit);
@@ -90,17 +121,19 @@ 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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ 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, regs->gpo_data_a + bank,
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;
+ const struct adp5585_regs *regs = adp5585_gpio->regs;
unsigned int bit, reg, mask, val;
/*
@@ -108,8 +141,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 = regs->rpull_cfg_a + bit / 8;
mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
val = bias << (bit % 8);
@@ -119,22 +154,24 @@ 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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->info->bit(off);
return regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_GPO_OUT_MODE_A + bank, bit,
+ regs->gpo_out_a + bank, 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_regs *regs = adp5585_gpio->regs;
+ unsigned int bank = adp5585_gpio->info->bank(off);
+ unsigned int bit = adp5585_gpio->info->bit(off);
return regmap_update_bits(adp5585_gpio->regmap,
- ADP5585_DEBOUNCE_DIS_A + bank, bit,
+ regs->debounce_dis_a + bank, bit,
debounce ? 0 : bit);
}
@@ -175,6 +212,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;
@@ -185,6 +223,11 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
adp5585_gpio->regmap = adp5585->regmap;
+ adp5585_gpio->regs = adp5585->info->regs;
+
+ 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);
@@ -199,7 +242,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->info->max_cols + adp5585->info->max_rows;
gc->label = pdev->name;
gc->owner = THIS_MODULE;
@@ -211,8 +254,20 @@ 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,
+ .has_bias_hole = true,
+};
+
+static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
+ .bank = adp5589_gpio_bank,
+ .bit = adp5589_gpio_bit,
+};
+
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);
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 08/17] pwm: adp5585: add support for adp5589
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (6 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
` (11 subsequent siblings)
19 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 | 51 ++++++++++++++++++++++++++++++++---------------
1 file changed, 35 insertions(+), 16 deletions(-)
diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
index c8821035b7c1412a55a642e6e8a46b66e693a5af..cc8ac8f9e5669b4ffca06d4117a29f030393f48f 100644
--- a/drivers/pwm/pwm-adp5585.c
+++ b/drivers/pwm/pwm-adp5585.c
@@ -32,21 +32,30 @@
#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 {
+ const struct adp5585_regs *regs;
+ struct regmap *regmap;
+};
+
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);
+ const struct adp5585_regs *regs = adp5585_pwm->regs;
+ struct regmap *regmap = adp5585_pwm->regmap;
/* Configure the R3 pin as PWM output. */
- return regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
+ return regmap_update_bits(regmap, regs->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);
+ const struct adp5585_regs *regs = adp5585_pwm->regs;
+ struct regmap *regmap = adp5585_pwm->regmap;
- regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
+ regmap_update_bits(regmap, regs->ext_cfg,
ADP5585_R3_EXTEND_CFG_MASK,
ADP5585_R3_EXTEND_CFG_GPIO4);
}
@@ -55,14 +64,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_regs *regs = adp5585_pwm->regs;
+ 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, regs->pwm_cfg, ADP5585_PWM_EN);
return 0;
}
@@ -83,41 +94,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, regs->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, regs->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, regs->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, regs->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_regs *regs = adp5585_pwm->regs;
+ 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, regs->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, regs->pwm_ont_low, &on_off, 2);
if (ret)
return ret;
on = le16_to_cpu(on_off);
@@ -127,7 +140,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, regs->pwm_cfg, &val);
state->enabled = !!(val & ADP5585_PWM_EN);
return 0;
@@ -144,16 +157,21 @@ static int adp5585_pwm_probe(struct platform_device *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->regs = adp5585->info->regs;
+
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);
@@ -165,6 +183,7 @@ static int adp5585_pwm_probe(struct platform_device *pdev)
static const struct platform_device_id adp5585_pwm_id_table[] = {
{ "adp5585-pwm" },
+ { "adp5589-pwm" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (7 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 08/17] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:28 ` Laurent Pinchart
2025-04-29 15:03 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 10/17] mfd: adp5585: add support for key events Nuno Sá via B4 Relay
` (10 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
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 d690a411b3a4307f6dd5f4a432a357e89fefabb0..b7c36e2d4bfc4e75c856a0b8b9c842627bf3b1b2 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-keys:
+ description:
+ Specifies a maximum of 2 keys that can be used to unlock the keypad.
+ If this property is set, the keyboard will be locked and only unlocked
+ after these keys 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-keys:
+ description:
+ Defines the trigger events (key presses) that can generate reset
+ conditions one the reset1 block.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 3
+
+ adi,reset2-keys:
+ description:
+ Defines the trigger events (key 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-passtrough-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-keys
+ adi,reset1-active-high:
+ - adi,reset1-keys
+ adi,rst-passtrough-enable:
+ - adi,reset1-keys
+ adi,reset2-active-high:
+ - adi,reset2-keys
+
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-keys: false
+ adi,unlock-trigger-sec: false
gpio-reserved-ranges: false
-
+ adi,keypad-pins:
+ minItems: 2
+ maxItems: 11
+ items:
+ minimum: 0
+ maximum: 10
+ adi,reset1-keys:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 30
+ - minimum: 37
+ maximum: 47
+ adi,reset2-keys:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 30
+ - minimum: 37
+ maximum: 47
- if:
properties:
compatible:
@@ -81,6 +197,25 @@ allOf:
- adi,adp5585-02
then:
properties:
+ adi,unlock-keys: 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-keys:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 25
+ - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
+ adi,reset2-keys:
+ 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-keys:
+ items:
+ anyOf:
+ - minimum: 1
+ maximum: 88
+ - minimum: 97
+ maximum: 115
+ adi,reset2-keys:
+ 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-keys = <1 43>;
+ adi,reset2-keys = <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] 81+ messages in thread
* [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (8 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:33 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 11/17] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
` (9 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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>
This adds support for key events. Basically, it adds support for all the
FW parsing and configuration of the keys (also making sure there's no
overlaps). They can either be part of a matrix keymap (in which case
events will be handled by an input device) or they can be gpi's (in which
case events will be handled by the gpiochip device via gpio_keys). There's
also support for unlock keys as for reset keys.
This patch adds all the foundation needed by subsequent changes where the
child devices (actually handling the events) can "register" a callback
to handle the event.
Also to note that enabling the internal oscillator in now done as part of
adp5585_setup().
We also enable/disable the IRQ (if present) in the corresponding PM
event.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/mfd/adp5585.c | 551 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/mfd/adp5585.h | 96 ++++++++
2 files changed, 642 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index c3586c0d6aa2e7e7d94667993410610be7fc3672..c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -8,11 +8,13 @@
*/
#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mfd/adp5585.h>
#include <linux/mfd/core.h>
+#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -205,11 +207,19 @@ static const struct adp5585_regs adp5585_regs = {
.gpo_out_a = ADP5585_GPO_OUT_MODE_A,
.gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
.gpi_stat_a = ADP5585_GPI_STATUS_A,
+ .gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
+ .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
.pwm_cfg = ADP5585_PWM_CFG,
.pwm_offt_low = ADP5585_PWM_OFFT_LOW,
.pwm_ont_low = ADP5585_PWM_ONT_LOW,
+ .reset_cfg = ADP5585_RESET_CFG,
.gen_cfg = ADP5585_GENERAL_CFG,
.ext_cfg = ADP5585_PIN_CONFIG_C,
+ .pin_cfg_a = ADP5585_PIN_CONFIG_A,
+ .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
+ .int_en = ADP5585_INT_EN,
+ .reset1_event_a = ADP5585_RESET1_EVENT_A,
+ .reset2_event_a = ADP5585_RESET2_EVENT_A,
};
static const struct adp5585_regs adp5589_regs = {
@@ -219,11 +229,19 @@ static const struct adp5585_regs adp5589_regs = {
.gpo_out_a = ADP5589_GPO_OUT_MODE_A,
.gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
.gpi_stat_a = ADP5589_GPI_STATUS_A,
+ .gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
+ .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
.pwm_cfg = ADP5589_PWM_CFG,
.pwm_offt_low = ADP5589_PWM_OFFT_LOW,
.pwm_ont_low = ADP5589_PWM_ONT_LOW,
+ .reset_cfg = ADP5589_RESET_CFG,
.gen_cfg = ADP5589_GENERAL_CFG,
.ext_cfg = ADP5589_PIN_CONFIG_D,
+ .pin_cfg_a = ADP5589_PIN_CONFIG_A,
+ .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
+ .int_en = ADP5589_INT_EN,
+ .reset1_event_a = ADP5589_RESET1_EVENT_A,
+ .reset2_event_a = ADP5589_RESET2_EVENT_A,
};
static const struct adp5585_info adp5585_info = {
@@ -234,6 +252,8 @@ static const struct adp5585_info adp5585_info = {
.regs = &adp5585_regs,
.max_rows = ADP5585_MAX_ROW_NUM,
.max_cols = ADP5585_MAX_COL_NUM,
+ .gpi_ev_base = ADP5585_GPI_EVENT_START,
+ .gpi_ev_end = ADP5585_GPI_EVENT_END,
};
static const struct adp5585_info adp5585_01_info = {
@@ -241,9 +261,12 @@ static const struct adp5585_info adp5585_01_info = {
.regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
.n_devs = ARRAY_SIZE(adp5585_devs),
.id = ADP5585_MAN_ID_VALUE,
+ .has_row5 = true,
.regs = &adp5585_regs,
.max_rows = ADP5585_MAX_ROW_NUM,
.max_cols = ADP5585_MAX_COL_NUM,
+ .gpi_ev_base = ADP5585_GPI_EVENT_START,
+ .gpi_ev_end = ADP5585_GPI_EVENT_END,
};
static const struct adp5585_info adp5585_02_info = {
@@ -254,6 +277,8 @@ static const struct adp5585_info adp5585_02_info = {
.regs = &adp5585_regs,
.max_rows = ADP5585_MAX_ROW_NUM,
.max_cols = ADP5585_MAX_COL_NUM,
+ .gpi_ev_base = ADP5585_GPI_EVENT_START,
+ .gpi_ev_end = ADP5585_GPI_EVENT_END,
};
static const struct adp5585_info adp5585_04_info = {
@@ -264,6 +289,8 @@ static const struct adp5585_info adp5585_04_info = {
.regs = &adp5585_regs,
.max_rows = ADP5585_MAX_ROW_NUM,
.max_cols = ADP5585_MAX_COL_NUM,
+ .gpi_ev_base = ADP5585_GPI_EVENT_START,
+ .gpi_ev_end = ADP5585_GPI_EVENT_END,
};
static const struct adp5585_info adp5589_info = {
@@ -271,9 +298,13 @@ static const struct adp5585_info adp5589_info = {
.regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
.n_devs = ARRAY_SIZE(adp5589_devs),
.id = ADP5589_MAN_ID_VALUE,
+ .has_row5 = true,
+ .has_unlock = true,
.regs = &adp5589_regs,
.max_rows = ADP5589_MAX_ROW_NUM,
.max_cols = ADP5589_MAX_COL_NUM,
+ .gpi_ev_base = ADP5589_GPI_EVENT_START,
+ .gpi_ev_end = ADP5589_GPI_EVENT_END,
};
static const struct adp5585_info adp5589_01_info = {
@@ -281,9 +312,13 @@ static const struct adp5585_info adp5589_01_info = {
.regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
.n_devs = ARRAY_SIZE(adp5589_devs),
.id = ADP5589_MAN_ID_VALUE,
+ .has_row5 = true,
+ .has_unlock = true,
.regs = &adp5589_regs,
.max_rows = ADP5589_MAX_ROW_NUM,
.max_cols = ADP5589_MAX_COL_NUM,
+ .gpi_ev_base = ADP5589_GPI_EVENT_START,
+ .gpi_ev_end = ADP5589_GPI_EVENT_END,
};
static const struct adp5585_info adp5589_02_info = {
@@ -291,11 +326,379 @@ static const struct adp5585_info adp5589_02_info = {
.regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
.n_devs = ARRAY_SIZE(adp5589_devs),
.id = ADP5589_MAN_ID_VALUE,
+ .has_row5 = true,
+ .has_unlock = true,
.regs = &adp5589_regs,
.max_rows = ADP5589_MAX_ROW_NUM,
.max_cols = ADP5589_MAX_COL_NUM,
+ .gpi_ev_base = ADP5589_GPI_EVENT_START,
+ .gpi_ev_end = ADP5589_GPI_EVENT_END,
};
+static int adp5585_keys_validate_key(const struct adp5585_dev *adp5585, u32 key,
+ bool is_gpi)
+{
+ const struct adp5585_info *info = adp5585->info;
+ struct device *dev = adp5585->dev;
+ u32 row, col;
+
+ if (is_gpi) {
+ u32 gpi = key - adp5585->info->gpi_ev_base;
+
+ if (!info->has_row5 && gpi == ADP5585_ROW5)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid unlock/reset GPI(%u) not supported\n",
+ gpi);
+
+ /* check if it's being used in the keypad */
+ if (test_bit(gpi, adp5585->keypad))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid unlock/reset GPI(%u) being used in the keypad\n",
+ gpi);
+
+ return 0;
+ }
+
+ row = (key - 1) / info->max_cols;
+ col = (key - 1) % info->max_cols;
+
+ /* both the row and col must be part of the keypad */
+ if (test_bit(row, adp5585->keypad) &&
+ test_bit(col + info->max_rows, adp5585->keypad))
+ return 0;
+
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid unlock/reset key(%u) not used in the keypad\n", key);
+}
+
+static int adp5585_keys_parse_array(const struct adp5585_dev *adp5585,
+ const char *prop, u32 *keys, u32 *n_keys,
+ u32 max_keys, bool reset_key)
+{
+ const struct adp5585_info *info = adp5585->info;
+ struct device *dev = adp5585->dev;
+ unsigned int key, max_keypad;
+ int ret;
+
+ ret = device_property_count_u32(dev, prop);
+ if (ret < 0)
+ return 0;
+
+ *n_keys = ret;
+
+ if (!info->has_unlock && !reset_key)
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "Unlock keys not supported\n");
+
+ if (*n_keys > max_keys)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of keys(%u > %u) for %s\n",
+ *n_keys, max_keys, prop);
+
+ ret = device_property_read_u32_array(dev, prop, keys, *n_keys);
+ if (ret)
+ return ret;
+
+ max_keypad = adp5585->info->max_rows * adp5585->info->max_cols;
+
+ for (key = 0; key < *n_keys; key++) {
+ /* part of the keypad... */
+ if (in_range(keys[key], 1, max_keypad)) {
+ /* is it part of the keypad?! */
+ ret = adp5585_keys_validate_key(adp5585, keys[key],
+ false);
+ if (ret)
+ return ret;
+
+ continue;
+ }
+
+ /* part of gpio-keys... */
+ if (in_range(keys[key], adp5585->info->gpi_ev_base,
+ info->max_cols + info->max_rows)) {
+ /* is the GPI being used as part of the keypad?! */
+ ret = adp5585_keys_validate_key(adp5585, keys[key],
+ true);
+ if (ret)
+ return ret;
+
+ continue;
+ }
+
+ if (!reset_key && keys[key] == 127)
+ continue;
+
+ return dev_err_probe(dev, -EINVAL, "Invalid key(%u) for %s\n",
+ keys[key], prop);
+ }
+
+ return 0;
+}
+
+static int adp5585_keys_unlock_parse(struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ int ret;
+
+ ret = adp5585_keys_parse_array(adp5585, "adi,unlock-keys",
+ adp5585->unlock_keys,
+ &adp5585->nkeys_unlock,
+ ARRAY_SIZE(adp5585->unlock_keys), false);
+ if (ret)
+ return ret;
+ if (!adp5585->nkeys_unlock)
+ /* no unlock keys */
+ 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_keys_reset_parse(struct adp5585_dev *adp5585)
+{
+ const struct adp5585_info *info = adp5585->info;
+ struct device *dev = adp5585->dev;
+ u32 prop_val;
+ int ret;
+
+ ret = adp5585_keys_parse_array(adp5585, "adi,reset1-keys",
+ adp5585->reset1_keys,
+ &adp5585->nkeys_reset1,
+ ARRAY_SIZE(adp5585->reset1_keys), true);
+ if (ret)
+ return ret;
+
+ if (adp5585->nkeys_reset1 > 0) {
+ if (test_bit(ADP5585_ROW4, adp5585->keypad))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid reset1 output(R4) being used in the keypad\n");
+
+ if (device_property_read_bool(dev, "adi,reset1-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
+ }
+
+ ret = adp5585_keys_parse_array(adp5585, "adi,reset2-keys",
+ adp5585->reset2_keys,
+ &adp5585->nkeys_reset2,
+ ARRAY_SIZE(adp5585->reset2_keys), true);
+ if (ret)
+ return ret;
+
+ if (adp5585->nkeys_reset2 > 0) {
+ if (test_bit(info->max_rows + ADP5585_COL4, adp5585->keypad))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid reset2 output(C4) being used in the keypad\n");
+
+ if (device_property_read_bool(dev, "adi,reset2-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
+ }
+
+ if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
+ return 0;
+
+ if (device_property_read_bool(dev, "adi,rst-passtrough-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)
+{
+ const struct adp5585_info *info = adp5585->info;
+ unsigned int n_pins = info->max_cols + info->max_rows;
+ unsigned int *keypad_pins;
+ unsigned int prop_val;
+ int n_keys, key, ret;
+
+ adp5585->keypad = devm_bitmap_zalloc(dev, n_pins, GFP_KERNEL);
+ if (!adp5585->keypad)
+ return -ENOMEM;
+
+ if (device_property_present(dev, "#pwm-cells"))
+ adp5585->has_pwm = true;
+
+ n_keys = device_property_count_u32(dev, "adi,keypad-pins");
+ if (n_keys <= 0)
+ goto no_keypad;
+ if (n_keys > n_pins)
+ return -EINVAL;
+
+ keypad_pins = devm_kcalloc(dev, n_keys, sizeof(*keypad_pins),
+ GFP_KERNEL);
+ if (!keypad_pins)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(dev, "adi,keypad-pins",
+ keypad_pins, n_keys);
+ if (ret)
+ return ret;
+
+ for (key = 0; key < n_keys; key++) {
+ if (keypad_pins[key] >= n_pins)
+ return -EINVAL;
+ if (adp5585->has_pwm && keypad_pins[key] == ADP5585_ROW3)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid PWM pin being used in the keypad\n");
+ if (!info->has_row5 && keypad_pins[key] == ADP5585_ROW5)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid row5 being used in the keypad\n");
+ __set_bit(keypad_pins[key], adp5585->keypad);
+ }
+
+no_keypad:
+ ret = device_property_read_u32(dev, "adi,key-poll-ms", &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 10:
+ fallthrough;
+ case 20:
+ fallthrough;
+ case 30:
+ fallthrough;
+ case 40:
+ adp5585->key_poll_time = prop_val / 10 - 1;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for adi,key-poll-ms\n",
+ prop_val);
+ }
+ }
+
+ ret = adp5585_keys_unlock_parse(adp5585);
+ if (ret)
+ return ret;
+
+ return adp5585_keys_reset_parse(adp5585);
+}
+
+static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
+{
+ unsigned int i;
+
+ guard(mutex)(&adp5585->ev_lock);
+
+ 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);
+
+ if (key_val >= adp5585->info->gpi_ev_base &&
+ key_val <= adp5585->info->gpi_ev_end) {
+ unsigned int gpi = key_val - adp5585->info->gpi_ev_base;
+
+ if (adp5585->gpio_irq_handle)
+ adp5585->gpio_irq_handle(adp5585->gpio_dev, gpi,
+ key_press);
+ } else if (adp5585->keys_irq_handle) {
+ adp5585->keys_irq_handle(adp5585->input_dev, key_val,
+ key_press);
+ }
+ }
+}
+
+static irqreturn_t adp5585_irq(int irq, void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+ unsigned int status, ev_cnt;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (status & ADP5585_OVRFLOW_INT)
+ dev_err_ratelimited(adp5585->dev, "Event Overflow Error\n");
+
+ if (!(status & ADP5585_EVENT_INT))
+ goto out_irq;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
+ if (ret)
+ goto out_irq;
+
+ ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
+ if (!ev_cnt)
+ goto out_irq;
+
+ adp5585_report_events(adp5585, ev_cnt);
+out_irq:
+ regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
+ return IRQ_HANDLED;
+}
+
static void adp5585_osc_disable(void *data)
{
const struct adp5585_dev *adp5585 = data;
@@ -303,6 +706,128 @@ static void adp5585_osc_disable(void *data)
regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
}
+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->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,
+ ®_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
+ adp5585->key_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)
{
const struct adp5585_info *info;
@@ -321,6 +846,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return -ENODEV;
adp5585->info = info;
+ adp5585->dev = &i2c->dev;
+ adp5585->irq = i2c->irq;
adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
if (IS_ERR(adp5585->regmap))
@@ -337,12 +864,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
- ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
- ADP5585_OSC_EN);
+ ret = adp5585_parse_fw(&i2c->dev, adp5585);
if (ret)
return ret;
- ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
+ ret = adp5585_setup(adp5585);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
if (ret)
return ret;
@@ -353,13 +883,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;
@@ -368,11 +901,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 dffe1449de01dacf8fe78cf0e87d1f176d11f620..3ec542bed9c1c44899cc869d957557813e2d0aab 100644
--- a/include/linux/mfd/adp5585.h
+++ b/include/linux/mfd/adp5585.h
@@ -10,13 +10,21 @@
#define __MFD_ADP5585_H_
#include <linux/bits.h>
+#include <linux/cleanup.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 +40,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
@@ -60,6 +69,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
@@ -104,8 +114,17 @@
#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
+#define ADP5585_ROW3 3
+#define ADP5585_ROW4 4
+#define ADP5585_ROW5 5
+#define ADP5585_COL4 4
+#define ADP5585_MAX_UNLOCK_TIME_SEC 7
+#define ADP5585_GPI_EVENT_START 37
+#define ADP5585_GPI_EVENT_END 47
#define ADP5585_MAX_ROW_NUM 6
#define ADP5585_MAX_COL_NUM 5
@@ -124,18 +143,38 @@
#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
#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
+#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
#define ADP5589_MAX_REG ADP5589_INT_EN
+#define ADP5589_GPI_EVENT_START 97
+#define ADP5589_GPI_EVENT_END 115
#define ADP5589_MAX_ROW_NUM 8
#define ADP5589_MAX_COL_NUM 11
@@ -154,11 +193,19 @@ struct adp5585_regs {
unsigned int gpo_out_a;
unsigned int gpio_dir_a;
unsigned int gpi_stat_a;
+ unsigned int gpi_ev_a;
+ unsigned int gpi_int_lvl_a;
unsigned int pwm_cfg;
unsigned int pwm_offt_low;
unsigned int pwm_ont_low;
+ unsigned int reset_cfg;
unsigned int gen_cfg;
unsigned int ext_cfg;
+ unsigned int pin_cfg_a;
+ unsigned int poll_ptime_cfg;
+ unsigned int int_en;
+ unsigned int reset1_event_a;
+ unsigned int reset2_event_a;
};
struct adp5585_info {
@@ -169,6 +216,10 @@ struct adp5585_info {
unsigned int id;
u8 max_rows;
u8 max_cols;
+ u8 gpi_ev_base;
+ u8 gpi_ev_end;
+ bool has_row5;
+ bool has_unlock;
};
struct regmap;
@@ -176,6 +227,51 @@ struct regmap;
struct adp5585_dev {
struct regmap *regmap;
const struct adp5585_info *info;
+ struct device *dev;
+ unsigned long *keypad;
+ void (*gpio_irq_handle)(struct device *dev, unsigned int off,
+ bool key_press);
+ struct device *gpio_dev;
+ void (*keys_irq_handle)(struct device *dev, unsigned int off,
+ bool key_press);
+ struct device *input_dev;
+ /*
+ * Used to synchronize usage (and availability) of gpio_irq_handle()
+ * and keys_irq_handle().
+ */
+ struct mutex ev_lock;
+ int irq;
+ u32 key_poll_time;
+ u32 unlock_time;
+ u32 unlock_keys[2];
+ u32 nkeys_unlock;
+ u32 reset1_keys[3];
+ u32 nkeys_reset1;
+ u32 reset2_keys[2];
+ u32 nkeys_reset2;
+ u8 reset_cfg;
+ bool has_pwm;
};
+static inline void adp5585_gpio_ev_handle_set(struct adp5585_dev *adp5585,
+ void (*handle)(struct device *dev,
+ unsigned int off,
+ bool key_press),
+ struct device *gpio_dev)
+{
+ guard(mutex)(&adp5585->ev_lock);
+ adp5585->gpio_irq_handle = handle;
+ adp5585->gpio_dev = gpio_dev;
+}
+
+static inline void adp5585_keys_ev_handle_set(struct adp5585_dev *adp5585,
+ void (*handle)(struct device *dev,
+ unsigned int off,
+ bool key_press),
+ struct device *input_dev)
+{
+ guard(mutex)(&adp5585->ev_lock);
+ adp5585->keys_irq_handle = handle;
+ adp5585->input_dev = input_dev;
+}
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 11/17] gpio: adp5585: support gpi events
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (9 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 10/17] mfd: adp5585: add support for key events Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-17 12:28 ` Bartosz Golaszewski
2025-04-15 14:49 ` [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
` (8 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 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 .init_valid_mask() as we can't
blindly consume all available pins as GPIOs (example: some pins can be
used for forming a keymap matrix).
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-adp5585.c | 178 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 975cc95be54255ef5953bc61a8f7a7f174ea2626..73420dda8b4ccff751fa6fcd9ecf1ae60b395bf2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1251,6 +1251,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 d8f8d5513d7f6a9acf5bdecccacc89c4615ce237..a97babb2f8985238d8c401955f4cfcc54bd22948 100644
--- a/drivers/gpio/gpio-adp5585.c
+++ b/drivers/gpio/gpio-adp5585.c
@@ -11,6 +11,7 @@
#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>
@@ -27,6 +28,11 @@ struct adp5585_gpio_dev {
const struct adp5585_gpio_chip *info;
struct regmap *regmap;
const struct adp5585_regs *regs;
+ 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)
@@ -209,12 +215,155 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
};
}
+static int adp5585_gpio_init_valid_mask(struct gpio_chip *chip,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct device *dev = chip->parent;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+
+ bitmap_complement(valid_mask, adp5585->keypad, ngpios);
+ /*
+ * the keypad won't have (nor can't) have any special pin enabled which
+ * means bitmap_complement() will set them to 1. Make sure we clear them.
+ */
+ if (adp5585->has_pwm)
+ __clear_bit(ADP5585_ROW3, valid_mask);
+ if (adp5585->nkeys_reset1)
+ __clear_bit(ADP5585_ROW4, valid_mask);
+ if (adp5585->nkeys_reset2)
+ __clear_bit(adp5585->info->max_rows + ADP5585_COL4, valid_mask);
+ if (!adp5585->info->has_row5)
+ __clear_bit(ADP5585_ROW5, valid_mask);
+
+ return 0;
+}
+
+static void adp5585_gpio_key_event(struct device *dev, unsigned int off,
+ bool key_press)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = dev_get_drvdata(dev);
+ bool active_high = test_bit(off, &adp5585_gpio->irq_active_high);
+ unsigned int irq, irq_type;
+ struct irq_data *irqd;
+
+ irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off);
+ if (!irq)
+ return;
+
+ irqd = irq_get_irq_data(irq);
+ if (!irqd) {
+ dev_err(dev, "Could not get irq(%u) data\n", irq);
+ return;
+ }
+
+ 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);
+}
+
+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_regs *regs = adp5585_gpio->regs;
+ 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, regs->gpi_int_lvl_a + bank, bit,
+ active_high ? bit : 0);
+ regmap_update_bits(adp5585_gpio->regmap, regs->gpi_ev_a + bank, bit,
+ masked ? 0 : bit);
+ assign_bit(hwirq, &adp5585_gpio->irq_en, !masked);
+
+out_unlock:
+ mutex_unlock(&adp5585_gpio->bus_lock);
+}
+
+static void adp5585_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ __set_bit(hwirq, &adp5585_gpio->irq_mask);
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static void adp5585_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+ __clear_bit(hwirq, &adp5585_gpio->irq_mask);
+}
+
+static int adp5585_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ if (!(type & IRQ_TYPE_EDGE_BOTH))
+ return -EINVAL;
+
+ assign_bit(hwirq, &adp5585_gpio->irq_active_high,
+ type == IRQ_TYPE_EDGE_RISING);
+
+ irq_set_handler_locked(d, handle_edge_irq);
+ return 0;
+}
+
+static const struct irq_chip adp5585_irq_chip = {
+ .name = "adp5585",
+ .irq_mask = adp5585_irq_mask,
+ .irq_unmask = adp5585_irq_unmask,
+ .irq_bus_lock = adp5585_irq_bus_lock,
+ .irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock,
+ .irq_set_type = adp5585_irq_set_type,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static void adp5585_gpio_ev_handle_clean(void *adp5585)
+{
+ adp5585_gpio_ev_handle_set(adp5585, NULL, NULL);
+}
+
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;
@@ -239,6 +388,7 @@ 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->init_valid_mask = adp5585_gpio_init_valid_mask;
gc->can_sleep = true;
gc->base = -1;
@@ -246,6 +396,34 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
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;
+
+ platform_set_drvdata(pdev, adp5585_gpio);
+ adp5585_gpio_ev_handle_set(adp5585, adp5585_gpio_key_event,
+ dev);
+
+ ret = devm_add_action_or_reset(dev,
+ adp5585_gpio_ev_handle_clean,
+ adp5585);
+ 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)
--
2.49.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (10 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 11/17] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-19 2:44 ` Dmitry Torokhov
2025-04-21 9:35 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 13/17] Input: adp5589: remove the driver Nuno Sá via B4 Relay
` (7 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 | 221 ++++++++++++++++++++++++++++++++++
4 files changed, 234 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b5acf50fc6af4322dec0dad2169b46c6a1903e3c..48bd39a1a96d9c57145cf2560eec54248427fc89 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -549,6 +549,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/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..93961a9e822f8b10b1bca526b9486eed4ad7f8f7
--- /dev/null
+++ b/drivers/input/keyboard/adp5585-keys.c
@@ -0,0 +1,221 @@
+// 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 {
+ struct input_dev *input;
+ unsigned short keycode[ADP5589_MAX_KEYMAPSIZE];
+ struct device *dev;
+ int row_shift;
+ u8 max_rows;
+ u8 max_cols;
+};
+
+static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
+ struct adp5585_kpad *kpad)
+{
+ unsigned long row_map, col_map;
+ struct device *dev = kpad->dev;
+ u32 cols = 0, rows = 0;
+ int ret;
+
+ row_map = bitmap_read(adp5585->keypad, 0, kpad->max_rows);
+ col_map = bitmap_read(adp5585->keypad, kpad->max_rows, kpad->max_cols);
+ /*
+ * 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.
+ */
+ if (!bitmap_empty(&row_map, kpad->max_rows))
+ rows = find_last_bit(&row_map, kpad->max_rows) + 1;
+ if (!bitmap_empty(&col_map, kpad->max_cols))
+ cols = find_last_bit(&col_map, kpad->max_cols) + 1;
+
+ if (!rows && !cols)
+ return dev_err_probe(dev, -EINVAL,
+ "No rows or columns defined for the keypad\n");
+
+ if (cols && !rows)
+ return dev_err_probe(dev, -EINVAL,
+ "Cannot have columns with no rows!\n");
+
+ if (rows && !cols)
+ return dev_err_probe(dev, -EINVAL,
+ "Cannot have rows with no columns!\n");
+
+ ret = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
+ kpad->keycode, kpad->input);
+ if (ret)
+ return ret;
+
+ kpad->row_shift = get_count_order(cols);
+
+ if (device_property_present(kpad->dev, "autorepeat"))
+ __set_bit(EV_REP, kpad->input->evbit);
+
+ return 0;
+}
+
+static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
+ struct adp5585_kpad *kpad)
+{
+ unsigned long keys_bits, start = 0, nbits = kpad->max_rows;
+ const struct adp5585_regs *regs = adp5585->info->regs;
+ unsigned int i = 0, max_cols = kpad->max_cols;
+ int ret;
+
+ /*
+ * 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(adp5585->keypad, start, nbits);
+ if (keys_bits) {
+ ret = regmap_write(adp5585->regmap, regs->pin_cfg_a + i,
+ keys_bits);
+ if (ret)
+ return ret;
+ }
+
+ start += nbits;
+ if (max_cols > 8) {
+ nbits = 8;
+ max_cols -= nbits;
+ } else {
+ nbits = max_cols;
+ }
+
+ i++;
+ } while (start < kpad->max_rows + kpad->max_cols);
+
+ return 0;
+}
+
+static void adp5585_keys_ev_handle(struct device *dev, unsigned int key,
+ bool key_press)
+{
+ struct adp5585_kpad *kpad = dev_get_drvdata(dev);
+ unsigned int row, col, code;
+
+ row = (key - 1) / (kpad->max_cols);
+ col = (key - 1) % (kpad->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);
+}
+
+static void adp5585_keys_ev_handle_clean(void *adp5585)
+{
+ adp5585_keys_ev_handle_set(adp5585, NULL, NULL);
+}
+
+static int adp5585_keys_probe(struct platform_device *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 ret;
+
+ 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->max_cols = adp5585->info->max_cols;
+ kpad->max_rows = adp5585->info->max_rows;
+
+ kpad->input = devm_input_allocate_device(dev);
+ if (!kpad->input)
+ return -ENOMEM;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
+ if (ret)
+ return dev_err_probe(dev, ret, "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->dev.parent = dev;
+
+ input_set_drvdata(kpad->input, kpad);
+
+ 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);
+
+ ret = adp5585_keys_parse_fw(adp5585, kpad);
+ if (ret)
+ return ret;
+
+ ret = adp5585_keys_setup(adp5585, kpad);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, kpad);
+ adp5585_keys_ev_handle_set(adp5585, adp5585_keys_ev_handle, dev);
+ ret = devm_add_action_or_reset(dev, adp5585_keys_ev_handle_clean,
+ adp5585);
+ if (ret)
+ return ret;
+
+ return input_register_device(kpad->input);
+}
+
+static const struct platform_device_id adp5585_keys_id_table[] = {
+ { "adp5585-keys" },
+ { "adp5589-keys" },
+ { }
+};
+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] 81+ messages in thread
* [PATCH v2 13/17] Input: adp5589: remove the driver
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (11 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-19 2:45 ` Dmitry Torokhov
2025-04-21 9:40 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
` (6 subsequent siblings)
19 siblings, 2 replies; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
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] 81+ messages in thread
* [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (12 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 13/17] Input: adp5589: remove the driver Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:48 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
` (5 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b..667cc5bd0745f64eec60837ec3c00057af0cddeb 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -18,6 +18,7 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/types.h>
static const struct mfd_cell adp5585_devs[] = {
@@ -849,6 +850,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
adp5585->dev = &i2c->dev;
adp5585->irq = i2c->irq;
+ ret = devm_regulator_get_enable(&i2c->dev, "vdd");
+ if (ret)
+ return ret;
+
adp5585->regmap = devm_regmap_init_i2c(i2c, info->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] 81+ messages in thread
* [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (13 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:36 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 16/17] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
` (4 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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>
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 b7c36e2d4bfc4e75c856a0b8b9c842627bf3b1b2..218d6d6399d02adf678acff67b4ee246d833d209 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-keys: 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] 81+ messages in thread
* [PATCH v2 16/17] mfd: adp5585: add support for a reset pin
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (14 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:46 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
` (3 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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 | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
index 667cc5bd0745f64eec60837ec3c00057af0cddeb..2af110fb28054c8ad6709a9a42cb21919be433e7 100644
--- a/drivers/mfd/adp5585.c
+++ b/drivers/mfd/adp5585.c
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
#include <linux/mfd/adp5585.h>
#include <linux/mfd/core.h>
#include <linux/minmax.h>
@@ -833,6 +834,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
{
const struct adp5585_info *info;
struct adp5585_dev *adp5585;
+ struct gpio_desc *gpio;
unsigned int id;
int ret;
@@ -854,6 +856,16 @@ 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);
+
+ if (gpio) {
+ fsleep(30);
+ gpiod_set_value_cansleep(gpio, 0);
+ fsleep(60);
+ }
+
adp5585->regmap = devm_regmap_init_i2c(i2c, info->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] 81+ messages in thread
* [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (15 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 16/17] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
@ 2025-04-15 14:49 ` Nuno Sá via B4 Relay
2025-04-21 9:50 ` Laurent Pinchart
2025-04-15 15:56 ` [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Laurent Pinchart
` (2 subsequent siblings)
19 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá via B4 Relay @ 2025-04-15 14:49 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.
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 cc8ac8f9e5669b4ffca06d4117a29f030393f48f..85308257724a338da4d2416c8d01e48e08bd0856 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] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (16 preceding siblings ...)
2025-04-15 14:49 ` [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
@ 2025-04-15 15:56 ` Laurent Pinchart
2025-04-21 10:08 ` Laurent Pinchart
2025-04-16 9:02 ` Liu Ying
2025-05-01 12:00 ` Lee Jones
19 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-15 15:56 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, Krzysztof Kozlowski
Hi Nuno,
On Tue, Apr 15, 2025 at 03:49:16PM +0100, Nuno Sá via B4 Relay wrote:
> The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> and PWM. However, the gpio part of it was already supported as part of
> the keyboard driver:
>
> https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
>
> On top of that it also overlapped with my refactoring of the above driver [1]
> to drop usage of platform data and use FW properties instead.
>
> Now, it actually makes sense for this device to be supported under MFD
> and since the "legacy" input device depends on platform data that is not
> defined anywhere the plan in this series is to add support for the
> keyboard and adp5589 devices as part of the MFD driver. Once the MFD
> driver supports all that's supported in the Input one, we drop it...
>
> For DT Maintainers:
>
> The compatible for adp5589 is part of trivial devices. To me, it makes
> sense to remove it in the patch where we drop the driver but doing so
> would result in a warning when adding the same compatible for the MFD
> bindings. Hence, I remove it in that patch. Is that ok?
>
> Uwe:
>
> In my eval board, I could see that reading the GPIO value (when
> configured as input) does not work when OSC_EN is not set. Therefore,
> commit ("pwm: adp5585: don't control OSC_EN in the pwm driver") could
> very well have a Fixes tag. However I'm not 100% sure it's a real issue
> or something special to my eval board.
>
> It would be nice if Laurent or Liu could test the PWM bits or even
> check that the above is also an issue for their platform.
I'll give it a try, but it will need to wait until next week.
> [1]: https://lore.kernel.org/linux-input/d1395bd61ce58b3734121bca4e09605a3e997af3.camel@gmail.com/
>
> BTW the series is based on linux-next/master
>
> ---
> Changes in v2:
> - Patch 5:
> * Do not nest if:then:else::if:then.
> - Patch 6:
> * Make use of the adp5585 info variables and adp5589 volatile regs.
> - Patch 9:
> * Use standard "poll-interval" property (and move it before vendor
> properties).
> - Patch 10:
> * Make sure to include bitfield.h.
>
> - Link to v1: https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com
>
> ---
> Nuno Sá (17):
> dt-bindings: mfd: adp5585: ease on the required properties
> 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: add support for adp5589
> gpio: adp5585: add support for the ad5589 expander
> pwm: adp5585: add support for adp5589
> dt-bindings: mfd: adp5585: add properties for input events
> mfd: adp5585: add support for key events
> 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 | 299 +++++-
> drivers/input/keyboard/Kconfig | 21 +-
> drivers/input/keyboard/Makefile | 2 +-
> drivers/input/keyboard/adp5585-keys.c | 221 ++++
> drivers/input/keyboard/adp5589-keys.c | 1066 --------------------
> drivers/mfd/adp5585.c | 808 ++++++++++++++-
> drivers/pwm/pwm-adp5585.c | 57 +-
> include/linux/mfd/adp5585.h | 153 ++-
> 12 files changed, 1709 insertions(+), 1162 deletions(-)
> ---
> base-commit: 5b37f7bfff3b1582c34be8fb23968b226db71ebd
> change-id: 20250311-dev-adp5589-fw-e04cfd945286
> --
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (17 preceding siblings ...)
2025-04-15 15:56 ` [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Laurent Pinchart
@ 2025-04-16 9:02 ` Liu Ying
2025-04-16 10:03 ` Nuno Sá
2025-05-01 12:00 ` Lee Jones
19 siblings, 1 reply; 81+ messages in thread
From: Liu Ying @ 2025-04-16 9:02 UTC (permalink / raw)
To: nuno.sa, 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, Krzysztof Kozlowski
On 04/15/2025, Nuno Sá via B4 Relay wrote:
> The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> and PWM. However, the gpio part of it was already supported as part of
> the keyboard driver:
>
> https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
>
> On top of that it also overlapped with my refactoring of the above driver [1]
> to drop usage of platform data and use FW properties instead.
>
> Now, it actually makes sense for this device to be supported under MFD
> and since the "legacy" input device depends on platform data that is not
> defined anywhere the plan in this series is to add support for the
> keyboard and adp5589 devices as part of the MFD driver. Once the MFD
> driver supports all that's supported in the Input one, we drop it...
>
> For DT Maintainers:
>
> The compatible for adp5589 is part of trivial devices. To me, it makes
> sense to remove it in the patch where we drop the driver but doing so
> would result in a warning when adding the same compatible for the MFD
> bindings. Hence, I remove it in that patch. Is that ok?
>
> Uwe:
>
> In my eval board, I could see that reading the GPIO value (when
> configured as input) does not work when OSC_EN is not set. Therefore,
> commit ("pwm: adp5585: don't control OSC_EN in the pwm driver") could
> very well have a Fixes tag. However I'm not 100% sure it's a real issue
> or something special to my eval board.
>
> It would be nice if Laurent or Liu could test the PWM bits or even
> check that the above is also an issue for their platform.
With this v2 patch series, PWM backlight(controlled by ADP5585 pin R3) still
works for my i.MX93 11x11 EVK. Without this v2 patch series, if I change PWM
backlight to GPIO backlight(keep using pin R3), the GPIO backlight brightness
can be set to 0 or 1, meaning I can see the backlight is off or on. So, it
appears that GPIO output still works when OSC_EN is zero(I dumped GENERAL_CFG
register @0x3b as 0x00), though I didn't test GPIO input.
>
> [1]: https://lore.kernel.org/linux-input/d1395bd61ce58b3734121bca4e09605a3e997af3.camel@gmail.com/
>
> BTW the series is based on linux-next/master
>
> ---
> Changes in v2:
> - Patch 5:
> * Do not nest if:then:else::if:then.
> - Patch 6:
> * Make use of the adp5585 info variables and adp5589 volatile regs.
> - Patch 9:
> * Use standard "poll-interval" property (and move it before vendor
> properties).
> - Patch 10:
> * Make sure to include bitfield.h.
>
> - Link to v1: https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com
>
> ---
> Nuno Sá (17):
> dt-bindings: mfd: adp5585: ease on the required properties
> 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: add support for adp5589
> gpio: adp5585: add support for the ad5589 expander
> pwm: adp5585: add support for adp5589
> dt-bindings: mfd: adp5585: add properties for input events
> mfd: adp5585: add support for key events
> 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 | 299 +++++-
> drivers/input/keyboard/Kconfig | 21 +-
> drivers/input/keyboard/Makefile | 2 +-
> drivers/input/keyboard/adp5585-keys.c | 221 ++++
> drivers/input/keyboard/adp5589-keys.c | 1066 --------------------
> drivers/mfd/adp5585.c | 808 ++++++++++++++-
> drivers/pwm/pwm-adp5585.c | 57 +-
> include/linux/mfd/adp5585.h | 153 ++-
> 12 files changed, 1709 insertions(+), 1162 deletions(-)
> ---
> base-commit: 5b37f7bfff3b1582c34be8fb23968b226db71ebd
> change-id: 20250311-dev-adp5589-fw-e04cfd945286
> --
>
> Thanks!
> - Nuno Sá
>
>
--
Regards,
Liu Ying
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-04-16 9:02 ` Liu Ying
@ 2025-04-16 10:03 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-16 10:03 UTC (permalink / raw)
To: Liu Ying, nuno.sa, 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, Krzysztof Kozlowski
On Wed, 2025-04-16 at 17:02 +0800, Liu Ying wrote:
> On 04/15/2025, Nuno Sá via B4 Relay wrote:
> > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > and PWM. However, the gpio part of it was already supported as part of
> > the keyboard driver:
> >
> > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> >
> > On top of that it also overlapped with my refactoring of the above driver
> > [1]
> > to drop usage of platform data and use FW properties instead.
> >
> > Now, it actually makes sense for this device to be supported under MFD
> > and since the "legacy" input device depends on platform data that is not
> > defined anywhere the plan in this series is to add support for the
> > keyboard and adp5589 devices as part of the MFD driver. Once the MFD
> > driver supports all that's supported in the Input one, we drop it...
> >
> > For DT Maintainers:
> >
> > The compatible for adp5589 is part of trivial devices. To me, it makes
> > sense to remove it in the patch where we drop the driver but doing so
> > would result in a warning when adding the same compatible for the MFD
> > bindings. Hence, I remove it in that patch. Is that ok?
> >
> > Uwe:
> >
> > In my eval board, I could see that reading the GPIO value (when
> > configured as input) does not work when OSC_EN is not set. Therefore,
> > commit ("pwm: adp5585: don't control OSC_EN in the pwm driver") could
> > very well have a Fixes tag. However I'm not 100% sure it's a real issue
> > or something special to my eval board.
> >
> > It would be nice if Laurent or Liu could test the PWM bits or even
> > check that the above is also an issue for their platform.
>
> With this v2 patch series, PWM backlight(controlled by ADP5585 pin R3) still
> works for my i.MX93 11x11 EVK. Without this v2 patch series, if I change PWM
> backlight to GPIO backlight(keep using pin R3), the GPIO backlight brightness
> can be set to 0 or 1, meaning I can see the backlight is off or on. So, it
> appears that GPIO output still works when OSC_EN is zero(I dumped GENERAL_CFG
> register @0x3b as 0x00), though I didn't test GPIO input.
>
Yeah, the input case seems to be the problematic one... Anyways, thanks for
testing and confirm that PWM is not broken by this series.
- Nuno Sá
> >
> > [1]:
> > https://lore.kernel.org/linux-input/d1395bd61ce58b3734121bca4e09605a3e997af3.camel@gmail.com/
> >
> > BTW the series is based on linux-next/master
> >
> > ---
> > Changes in v2:
> > - Patch 5:
> > * Do not nest if:then:else::if:then.
> > - Patch 6:
> > * Make use of the adp5585 info variables and adp5589 volatile regs.
> > - Patch 9:
> > * Use standard "poll-interval" property (and move it before vendor
> > properties).
> > - Patch 10:
> > * Make sure to include bitfield.h.
> >
> > - Link to v1:
> > https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com
> >
> > ---
> > Nuno Sá (17):
> > dt-bindings: mfd: adp5585: ease on the required properties
> > 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: add support for adp5589
> > gpio: adp5585: add support for the ad5589 expander
> > pwm: adp5585: add support for adp5589
> > dt-bindings: mfd: adp5585: add properties for input events
> > mfd: adp5585: add support for key events
> > 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 | 299 +++++-
> > drivers/input/keyboard/Kconfig | 21 +-
> > drivers/input/keyboard/Makefile | 2 +-
> > drivers/input/keyboard/adp5585-keys.c | 221 ++++
> > drivers/input/keyboard/adp5589-keys.c | 1066 -----------------
> > ---
> > drivers/mfd/adp5585.c | 808 ++++++++++++++-
> > drivers/pwm/pwm-adp5585.c | 57 +-
> > include/linux/mfd/adp5585.h | 153 ++-
> > 12 files changed, 1709 insertions(+), 1162 deletions(-)
> > ---
> > base-commit: 5b37f7bfff3b1582c34be8fb23968b226db71ebd
> > change-id: 20250311-dev-adp5589-fw-e04cfd945286
> > --
> >
> > Thanks!
> > - Nuno Sá
> >
> >
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander
2025-04-15 14:49 ` [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander Nuno Sá via B4 Relay
@ 2025-04-17 12:27 ` Bartosz Golaszewski
2025-04-21 9:23 ` Laurent Pinchart
1 sibling, 0 replies; 81+ messages in thread
From: Bartosz Golaszewski @ 2025-04-17 12:27 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, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Tue, Apr 15, 2025 at 4:49 PM Nuno Sá via B4 Relay
<devnull+nuno.sa.analog.com@kernel.org> wrote:
>
> 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.
>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 11/17] gpio: adp5585: support gpi events
2025-04-15 14:49 ` [PATCH v2 11/17] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
@ 2025-04-17 12:28 ` Bartosz Golaszewski
0 siblings, 0 replies; 81+ messages in thread
From: Bartosz Golaszewski @ 2025-04-17 12:28 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, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Tue, Apr 15, 2025 at 4:49 PM Nuno Sá via B4 Relay
<devnull+nuno.sa.analog.com@kernel.org> wrote:
>
> 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 .init_valid_mask() as we can't
> blindly consume all available pins as GPIOs (example: some pins can be
> used for forming a keymap matrix).
>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
2025-04-15 14:49 ` [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
@ 2025-04-19 2:44 ` Dmitry Torokhov
2025-04-21 9:35 ` Laurent Pinchart
1 sibling, 0 replies; 81+ messages in thread
From: Dmitry Torokhov @ 2025-04-19 2:44 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 Tue, Apr 15, 2025 at 03:49:28PM +0100, Nuno Sá via B4 Relay wrote:
> 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.
This looks mostly good, just a few nits.
> +
> +static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
> + struct adp5585_kpad *kpad)
> +{
> + unsigned long row_map, col_map;
> + struct device *dev = kpad->dev;
> + u32 cols = 0, rows = 0;
> + int ret;
Please name variables holding error code or 0 "error".
> +
> + row_map = bitmap_read(adp5585->keypad, 0, kpad->max_rows);
> + col_map = bitmap_read(adp5585->keypad, kpad->max_rows, kpad->max_cols);
> + /*
> + * 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.
> + */
> + if (!bitmap_empty(&row_map, kpad->max_rows))
> + rows = find_last_bit(&row_map, kpad->max_rows) + 1;
> + if (!bitmap_empty(&col_map, kpad->max_cols))
> + cols = find_last_bit(&col_map, kpad->max_cols) + 1;
> +
> + if (!rows && !cols)
> + return dev_err_probe(dev, -EINVAL,
> + "No rows or columns defined for the keypad\n");
> +
> + if (cols && !rows)
> + return dev_err_probe(dev, -EINVAL,
> + "Cannot have columns with no rows!\n");
> +
> + if (rows && !cols)
> + return dev_err_probe(dev, -EINVAL,
> + "Cannot have rows with no columns!\n");
> +
> + ret = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
> + kpad->keycode, kpad->input);
> + if (ret)
> + return ret;
> +
> + kpad->row_shift = get_count_order(cols);
> +
> + if (device_property_present(kpad->dev, "autorepeat"))
"autorepeat" is essentially a boolean property, so please use
device_property_read_bool() here.
> + __set_bit(EV_REP, kpad->input->evbit);
> +
> + return 0;
> +}
> +
> +static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
> + struct adp5585_kpad *kpad)
> +{
> + unsigned long keys_bits, start = 0, nbits = kpad->max_rows;
> + const struct adp5585_regs *regs = adp5585->info->regs;
> + unsigned int i = 0, max_cols = kpad->max_cols;
> + int ret;
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(adp5585->keypad, start, nbits);
> + if (keys_bits) {
> + ret = regmap_write(adp5585->regmap, regs->pin_cfg_a + i,
> + keys_bits);
> + if (ret)
> + return ret;
> + }
> +
> + start += nbits;
> + if (max_cols > 8) {
> + nbits = 8;
> + max_cols -= nbits;
> + } else {
> + nbits = max_cols;
> + }
> +
> + i++;
> + } while (start < kpad->max_rows + kpad->max_cols);
> +
> + return 0;
> +}
> +
> +static void adp5585_keys_ev_handle(struct device *dev, unsigned int key,
> + bool key_press)
> +{
> + struct adp5585_kpad *kpad = dev_get_drvdata(dev);
> + unsigned int row, col, code;
> +
Please check that key is not 0 to avoid OOB access. While the device is
unlikely to ever send 0 cosmic rays may happen. And I guess we should
check the max as well.
> + row = (key - 1) / (kpad->max_cols);
> + col = (key - 1) % (kpad->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);
> +}
> +
> +static void adp5585_keys_ev_handle_clean(void *adp5585)
> +{
> + adp5585_keys_ev_handle_set(adp5585, NULL, NULL);
> +}
> +
> +static int adp5585_keys_probe(struct platform_device *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 ret;
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->max_cols = adp5585->info->max_cols;
> + kpad->max_rows = adp5585->info->max_rows;
> +
> + kpad->input = devm_input_allocate_device(dev);
> + if (!kpad->input)
> + return -ENOMEM;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
> + if (ret)
> + return dev_err_probe(dev, ret, "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->dev.parent = dev;
No need to set parent because devm_input_allocate_device() does it for
you.
> +
> + input_set_drvdata(kpad->input, kpad);
I do not see input_get_drvdata() anywhere, so maybe remove this call?
> +
> + 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);
> +
> + ret = adp5585_keys_parse_fw(adp5585, kpad);
> + if (ret)
> + return ret;
> +
> + ret = adp5585_keys_setup(adp5585, kpad);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, kpad);
> + adp5585_keys_ev_handle_set(adp5585, adp5585_keys_ev_handle, dev);
> + ret = devm_add_action_or_reset(dev, adp5585_keys_ev_handle_clean,
> + adp5585);
> + if (ret)
> + return ret;
> +
> + return input_register_device(kpad->input);
error = input_register_device(...);
if (error)
dev_err_probe(...);
return 0;
> +}
> +
> +static const struct platform_device_id adp5585_keys_id_table[] = {
> + { "adp5585-keys" },
> + { "adp5589-keys" },
> + { }
> +};
> +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");
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 13/17] Input: adp5589: remove the driver
2025-04-15 14:49 ` [PATCH v2 13/17] Input: adp5589: remove the driver Nuno Sá via B4 Relay
@ 2025-04-19 2:45 ` Dmitry Torokhov
2025-04-21 9:40 ` Laurent Pinchart
1 sibling, 0 replies; 81+ messages in thread
From: Dmitry Torokhov @ 2025-04-19 2:45 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
On Tue, Apr 15, 2025 at 03:49:29PM +0100, Nuno Sá via B4 Relay wrote:
> 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.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
@ 2025-04-21 8:56 ` Laurent Pinchart
2025-04-21 12:12 ` Nuno Sá
2025-04-21 18:57 ` Rob Herring (Arm)
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 8:56 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 Tue, Apr 15, 2025 at 03:49:17PM +0100, Nuno Sá via B4 Relay wrote:
> 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.
This seems to make sense.
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
I however expected changes in this series to *not* register MFD cells
for the devices not enabled in DT. Could you do so in v3, on top of this
patch ?
> 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:
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe
2025-04-15 14:49 ` [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
@ 2025-04-21 8:57 ` Laurent Pinchart
2025-04-21 12:14 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 8:57 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 Tue, Apr 15, 2025 at 03:49:18PM +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).
Setting this bit unconditionally increases power consumption. It should
only be set when needed.
> 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 160e0b38106a6d78f7d4b7c866cb603d96ea673e..f17b5f2474cac6a403556694066f438288264a49 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -110,6 +110,13 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> },
> };
>
> +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;
> @@ -138,6 +145,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> + 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,
> adp5585_devs, ARRAY_SIZE(adp5585_devs),
> NULL, 0, NULL);
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME()
2025-04-15 14:49 ` [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
@ 2025-04-21 9:03 ` Laurent Pinchart
2025-04-24 15:55 ` Lee Jones
1 sibling, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:03 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
On Tue, Apr 15, 2025 at 03:49:20PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Use the helper macro. No functional change intended...
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/mfd/adp5585.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index f17b5f2474cac6a403556694066f438288264a49..fafe3ad93ea196e1eb8e79fecba58f36f12167eb 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>
> @@ -18,8 +19,10 @@
> #include <linux/types.h>
>
> static const struct mfd_cell adp5585_devs[] = {
> - { .name = "adp5585-gpio", },
> - { .name = "adp5585-pwm", },
> + MFD_CELL_NAME("adp5585-keys"),
> + MFD_CELL_NAME("adp5585-gpio"),
> + MFD_CELL_NAME("adp5585-pwm"),
> +
> };
>
> static const struct regmap_range adp5585_volatile_ranges[] = {
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
2025-04-15 14:49 ` [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
@ 2025-04-21 9:07 ` Laurent Pinchart
2025-04-21 18:59 ` Rob Herring (Arm)
1 sibling, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:07 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 Tue, Apr 15, 2025 at 03:49:21PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> programmable logic, reset generator, and PWM generator.
>
> We can't really have adp5589 devices fallback to adp5585 (which have
> less pins) because there are some significant differences in the register
> map.
>
> 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..d690a411b3a4307f6dd5f4a432a357e89fefabb0 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-03
> + - adi,adp5585-04
> + - adi,adp5585-02
Alphabetical order please. With this fixed,
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + 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
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-15 14:49 ` [PATCH v2 06/17] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
@ 2025-04-21 9:15 ` Laurent Pinchart
2025-04-21 12:21 ` Nuno Sá
2025-04-24 16:18 ` Lee Jones
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:15 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 Tue, Apr 15, 2025 at 03:49:22PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> programmable logic, reset generator, and PWM generator.
>
> This patch adds the foundation to add support for the adp5589 gpio and pwm
> drivers. Most importantly, we need to differentiate between some
> registers addresses. It also hints to future keymap support.
Please split this in two patches, one that reworks the driver to support
different register addresses, and one that adds adp5589 support.
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> include/linux/mfd/adp5585.h | 57 ++++++++++-
> 2 files changed, 268 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d94667993410610be7fc3672 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
>
> };
>
> +static const struct mfd_cell adp5589_devs[] = {
> + MFD_CELL_NAME("adp5589-keys"),
> + 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),
> };
> @@ -34,6 +41,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:
> @@ -77,10 +93,52 @@ 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,
> +};
> +
> enum adp5585_regmap_type {
> ADP5585_REGMAP_00,
> ADP5585_REGMAP_02,
> ADP5585_REGMAP_04,
> + ADP5589_REGMAP_00,
> + ADP5589_REGMAP_01,
> + ADP5589_REGMAP_02,
> };
>
> static const struct regmap_config adp5585_regmap_configs[] = {
> @@ -111,6 +169,131 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> .reg_defaults_raw = adp5585_regmap_defaults_04,
> .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> },
> + [ADP5589_REGMAP_00] = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ADP5589_MAX_REG,
> + .volatile_table = &adp5589_volatile_regs,
> + .cache_type = REGCACHE_MAPLE,
> + .reg_defaults_raw = adp5589_regmap_defaults_00,
> + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_00),
> + },
> + [ADP5589_REGMAP_01] = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ADP5589_MAX_REG,
> + .volatile_table = &adp5589_volatile_regs,
> + .cache_type = REGCACHE_MAPLE,
> + .reg_defaults_raw = adp5589_regmap_defaults_01,
> + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_01),
> + },
> + [ADP5589_REGMAP_02] = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ADP5589_MAX_REG,
> + .volatile_table = &adp5589_volatile_regs,
> + .cache_type = REGCACHE_MAPLE,
> + .reg_defaults_raw = adp5589_regmap_defaults_02,
> + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_02),
> + },
> +};
> +
> +static const struct adp5585_regs adp5585_regs = {
> + .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,
> + .pwm_cfg = ADP5585_PWM_CFG,
> + .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> + .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> + .gen_cfg = ADP5585_GENERAL_CFG,
> + .ext_cfg = ADP5585_PIN_CONFIG_C,
> +};
Why does this need to be stored in this driver, and not in the drivers
for the gpio and pwm cells ? If the kernel is compiled without e.g. the
adp5585-pwm driver, we shouldn't waste memory here by adding data that
only the adp5585-pwm driver needs.
> +
> +static const struct adp5585_regs adp5589_regs = {
> + .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,
> + .pwm_cfg = ADP5589_PWM_CFG,
> + .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> + .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> + .gen_cfg = ADP5589_GENERAL_CFG,
> + .ext_cfg = ADP5589_PIN_CONFIG_D,
> +};
> +
> +static const struct adp5585_info adp5585_info = {
> + .adp5585_devs = adp5585_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .n_devs = ARRAY_SIZE(adp5585_devs),
> + .id = ADP5585_MAN_ID_VALUE,
> + .regs = &adp5585_regs,
> + .max_rows = ADP5585_MAX_ROW_NUM,
> + .max_cols = ADP5585_MAX_COL_NUM,
Same here, the max_rows and max_cols fields don't seem to belong to this
driver.
> +};
> +
> +static const struct adp5585_info adp5585_01_info = {
> + .adp5585_devs = adp5585_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .n_devs = ARRAY_SIZE(adp5585_devs),
> + .id = ADP5585_MAN_ID_VALUE,
> + .regs = &adp5585_regs,
> + .max_rows = ADP5585_MAX_ROW_NUM,
> + .max_cols = ADP5585_MAX_COL_NUM,
> +};
> +
> +static const struct adp5585_info adp5585_02_info = {
> + .adp5585_devs = adp5585_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> + .n_devs = ARRAY_SIZE(adp5585_devs),
> + .id = ADP5585_MAN_ID_VALUE,
> + .regs = &adp5585_regs,
> + .max_rows = ADP5585_MAX_ROW_NUM,
> + .max_cols = ADP5585_MAX_COL_NUM,
> +};
> +
> +static const struct adp5585_info adp5585_04_info = {
> + .adp5585_devs = adp5585_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> + .n_devs = ARRAY_SIZE(adp5585_devs),
> + .id = ADP5585_MAN_ID_VALUE,
> + .regs = &adp5585_regs,
> + .max_rows = ADP5585_MAX_ROW_NUM,
> + .max_cols = ADP5585_MAX_COL_NUM,
> +};
> +
> +static const struct adp5585_info adp5589_info = {
> + .adp5585_devs = adp5589_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> + .n_devs = ARRAY_SIZE(adp5589_devs),
> + .id = ADP5589_MAN_ID_VALUE,
> + .regs = &adp5589_regs,
> + .max_rows = ADP5589_MAX_ROW_NUM,
> + .max_cols = ADP5589_MAX_COL_NUM,
> +};
> +
> +static const struct adp5585_info adp5589_01_info = {
> + .adp5585_devs = adp5589_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> + .n_devs = ARRAY_SIZE(adp5589_devs),
> + .id = ADP5589_MAN_ID_VALUE,
> + .regs = &adp5589_regs,
> + .max_rows = ADP5589_MAX_ROW_NUM,
> + .max_cols = ADP5589_MAX_COL_NUM,
> +};
> +
> +static const struct adp5585_info adp5589_02_info = {
> + .adp5585_devs = adp5589_devs,
> + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> + .n_devs = ARRAY_SIZE(adp5589_devs),
> + .id = ADP5589_MAN_ID_VALUE,
> + .regs = &adp5589_regs,
> + .max_rows = ADP5589_MAX_ROW_NUM,
> + .max_cols = ADP5589_MAX_COL_NUM,
> };
>
> static void adp5585_osc_disable(void *data)
> @@ -122,7 +305,7 @@ static void adp5585_osc_disable(void *data)
>
> static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> - const struct regmap_config *regmap_config;
> + const struct adp5585_info *info;
> struct adp5585_dev *adp5585;
> unsigned int id;
> int ret;
> @@ -133,8 +316,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
>
> i2c_set_clientdata(i2c, adp5585);
>
> - regmap_config = i2c_get_match_data(i2c);
> - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> + info = i2c_get_match_data(i2c);
> + if (!info)
> + return -ENODEV;
Can this happen ?
> +
> + adp5585->info = info;
Drop the local info variable and assign the value to adp5585->info
directly.
> +
> + adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> "Failed to initialize register map\n");
> @@ -144,7 +332,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);
>
> @@ -158,8 +347,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return ret;
>
> ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> - adp5585_devs, ARRAY_SIZE(adp5585_devs),
> - NULL, 0, NULL);
> + adp5585->info->adp5585_devs,
> + adp5585->info->n_devs, NULL, 0, NULL);
> if (ret)
> return dev_err_probe(&i2c->dev, ret,
> "Failed to add child devices\n");
> @@ -191,19 +380,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
> static const struct of_device_id adp5585_of_match[] = {
> {
> .compatible = "adi,adp5585-00",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .data = &adp5585_info,
> }, {
> .compatible = "adi,adp5585-01",
> - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> + .data = &adp5585_01_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,
> + }, {
> + .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 016033cd68e46757aca86d21dd37025fd354b801..dffe1449de01dacf8fe78cf0e87d1f176d11f620 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -104,9 +104,11 @@
> #define ADP5585_INT_CFG BIT(1)
> #define ADP5585_RST_CFG BIT(0)
> #define ADP5585_INT_EN 0x3c
> -
> #define ADP5585_MAX_REG ADP5585_INT_EN
>
> +#define ADP5585_MAX_ROW_NUM 6
> +#define ADP5585_MAX_COL_NUM 5
> +
> /*
> * 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
> @@ -117,10 +119,63 @@
> #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_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
Indentation looks wrong.
> +#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_GENERAL_CFG 0x4d
> +#define ADP5589_INT_EN 0x4e
> +#define ADP5589_MAX_REG ADP5589_INT_EN
> +
> +#define ADP5589_MAX_ROW_NUM 8
> +#define ADP5589_MAX_COL_NUM 11
> +
> +/*
> + * 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_regs {
> + 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 pwm_cfg;
> + unsigned int pwm_offt_low;
> + unsigned int pwm_ont_low;
> + unsigned int gen_cfg;
> + unsigned int ext_cfg;
> +};
> +
> +struct adp5585_info {
> + const struct mfd_cell *adp5585_devs;
> + const struct regmap_config *regmap_config;
> + const struct adp5585_regs *regs;
> + unsigned int n_devs;
> + unsigned int id;
> + u8 max_rows;
> + u8 max_cols;
> +};
> +
> struct regmap;
>
> struct adp5585_dev {
> struct regmap *regmap;
> + const struct adp5585_info *info;
> };
>
> #endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander
2025-04-15 14:49 ` [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander Nuno Sá via B4 Relay
2025-04-17 12:27 ` Bartosz Golaszewski
@ 2025-04-21 9:23 ` Laurent Pinchart
2025-04-21 12:22 ` Nuno Sá
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:23 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 Tue, Apr 15, 2025 at 03:49:23PM +0100, Nuno Sá via B4 Relay wrote:
> 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.
Same comment as for 06/17, splitting the patch in two will make it
easier to review.
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/gpio/gpio-adp5585.c | 121 ++++++++++++++++++++++++++++++++------------
> 1 file changed, 88 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
> index d5c0f1b267c82a5002b50cbb7a108166439e4785..d8f8d5513d7f6a9acf5bdecccacc89c4615ce237 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,21 +15,49 @@
> #include <linux/regmap.h>
> #include <linux/types.h>
>
> -#define ADP5585_GPIO_MAX 11
> +struct adp5585_gpio_chip {
> + unsigned int max_gpio;
> + int (*bank)(unsigned int off);
> + int (*bit)(unsigned int off);
> + bool has_bias_hole;
> +};
>
> struct adp5585_gpio_dev {
> struct gpio_chip gpio_chip;
> + const struct adp5585_gpio_chip *info;
> struct regmap *regmap;
> + const struct adp5585_regs *regs;
> };
>
> +static int adp5585_gpio_bank(unsigned int off)
> +{
> + return ADP5585_BANK(off);
While at it, let's move the ADP558[59]_{BANK,BIT} macros to this file,
and name them ADP558[59]_GPIO_{BANK_BIT}.
> +}
> +
> +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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->info->bit(off);
> unsigned int val;
>
> - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
> + regmap_read(adp5585_gpio->regmap, regs->gpio_dir_a + bank, &val);
>
> return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
> }
> @@ -36,35 +65,37 @@ static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
> 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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->info->bit(off);
>
> - return regmap_clear_bits(adp5585_gpio->regmap,
> - ADP5585_GPIO_DIRECTION_A + bank, bit);
> + return regmap_clear_bits(adp5585_gpio->regmap, regs->gpio_dir_a + bank,
> + bit);
> }
>
> 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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->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, regs->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, regs->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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->info->bit(off);
> unsigned int reg;
> unsigned int val;
>
> @@ -79,8 +110,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, regs->gpio_dir_a + bank, &val);
> + reg = val & bit ? regs->gpo_data_a : regs->gpi_stat_a;
> regmap_read(adp5585_gpio->regmap, reg + bank, &val);
>
> return !!(val & bit);
> @@ -90,17 +121,19 @@ 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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + 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, regs->gpo_data_a + bank,
> 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;
> + const struct adp5585_regs *regs = adp5585_gpio->regs;
> unsigned int bit, reg, mask, val;
>
> /*
> @@ -108,8 +141,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 = regs->rpull_cfg_a + bit / 8;
> mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
> val = bias << (bit % 8);
>
> @@ -119,22 +154,24 @@ 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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->info->bit(off);
>
> return regmap_update_bits(adp5585_gpio->regmap,
> - ADP5585_GPO_OUT_MODE_A + bank, bit,
> + regs->gpo_out_a + bank, 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_regs *regs = adp5585_gpio->regs;
> + unsigned int bank = adp5585_gpio->info->bank(off);
> + unsigned int bit = adp5585_gpio->info->bit(off);
>
> return regmap_update_bits(adp5585_gpio->regmap,
> - ADP5585_DEBOUNCE_DIS_A + bank, bit,
> + regs->debounce_dis_a + bank, bit,
> debounce ? 0 : bit);
> }
>
> @@ -175,6 +212,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;
> @@ -185,6 +223,11 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
> return -ENOMEM;
>
> adp5585_gpio->regmap = adp5585->regmap;
> + adp5585_gpio->regs = adp5585->info->regs;
> +
> + 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);
>
> @@ -199,7 +242,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->info->max_cols + adp5585->info->max_rows;
> gc->label = pdev->name;
> gc->owner = THIS_MODULE;
>
> @@ -211,8 +254,20 @@ 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,
> + .has_bias_hole = true,
> +};
> +
> +static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
> + .bank = adp5589_gpio_bank,
> + .bit = adp5589_gpio_bit,
> +};
> +
> 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);
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events
2025-04-15 14:49 ` [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
@ 2025-04-21 9:28 ` Laurent Pinchart
2025-04-21 12:24 ` Nuno Sá
2025-04-29 15:03 ` Rob Herring (Arm)
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:28 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 Tue, Apr 15, 2025 at 03:49:25PM +0100, Nuno Sá via B4 Relay wrote:
> 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.
>
> 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 d690a411b3a4307f6dd5f4a432a357e89fefabb0..b7c36e2d4bfc4e75c856a0b8b9c842627bf3b1b2 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-keys:
> + description:
> + Specifies a maximum of 2 keys that can be used to unlock the keypad.
> + If this property is set, the keyboard will be locked and only unlocked
> + after these keys are pressed. The value 127 serves as a wildcard which
> + means any key can be used for unlocking.
Is this really something that should be set through DT ? Aren't there
use cases for configuring this from userspace ? Same for some of the
other properties below.
> + $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-keys:
> + description:
> + Defines the trigger events (key presses) that can generate reset
> + conditions one the reset1 block.
> + $ref: /schemas/types.yaml#/definitions/uint32-array
> + minItems: 1
> + maxItems: 3
> +
> + adi,reset2-keys:
> + description:
> + Defines the trigger events (key 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-passtrough-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-keys
> + adi,reset1-active-high:
> + - adi,reset1-keys
> + adi,rst-passtrough-enable:
> + - adi,reset1-keys
> + adi,reset2-active-high:
> + - adi,reset2-keys
> +
> 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-keys: false
> + adi,unlock-trigger-sec: false
> gpio-reserved-ranges: false
> -
> + adi,keypad-pins:
> + minItems: 2
> + maxItems: 11
> + items:
> + minimum: 0
> + maximum: 10
> + adi,reset1-keys:
> + items:
> + anyOf:
> + - minimum: 1
> + maximum: 30
> + - minimum: 37
> + maximum: 47
> + adi,reset2-keys:
> + items:
> + anyOf:
> + - minimum: 1
> + maximum: 30
> + - minimum: 37
> + maximum: 47
> - if:
> properties:
> compatible:
> @@ -81,6 +197,25 @@ allOf:
> - adi,adp5585-02
> then:
> properties:
> + adi,unlock-keys: 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-keys:
> + items:
> + anyOf:
> + - minimum: 1
> + maximum: 25
> + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
> + adi,reset2-keys:
> + 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-keys:
> + items:
> + anyOf:
> + - minimum: 1
> + maximum: 88
> + - minimum: 97
> + maximum: 115
> + adi,reset2-keys:
> + 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-keys = <1 43>;
> + adi,reset2-keys = <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)
> + >;
> };
> };
>
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-15 14:49 ` [PATCH v2 10/17] mfd: adp5585: add support for key events Nuno Sá via B4 Relay
@ 2025-04-21 9:33 ` Laurent Pinchart
2025-04-21 12:32 ` Nuno Sá
2025-04-24 16:07 ` Lee Jones
0 siblings, 2 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:33 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 Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> This adds support for key events. Basically, it adds support for all the
> FW parsing and configuration of the keys (also making sure there's no
> overlaps). They can either be part of a matrix keymap (in which case
> events will be handled by an input device) or they can be gpi's (in which
> case events will be handled by the gpiochip device via gpio_keys). There's
> also support for unlock keys as for reset keys.
There's lots of code below specific to matrix keypad handling. Please
move it to the keypad driver, and keep this driver as lean as possible.
> This patch adds all the foundation needed by subsequent changes where the
> child devices (actually handling the events) can "register" a callback
> to handle the event.
>
> Also to note that enabling the internal oscillator in now done as part of
> adp5585_setup().
>
> We also enable/disable the IRQ (if present) in the corresponding PM
> event.
The patch does too many things. The base rule should be "one logical
change, one patch". At the very least the event reporting mechanism
and the keypad handling should be split in two different patches.
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 551 +++++++++++++++++++++++++++++++++++++++++++-
> include/linux/mfd/adp5585.h | 96 ++++++++
> 2 files changed, 642 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index c3586c0d6aa2e7e7d94667993410610be7fc3672..c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -8,11 +8,13 @@
> */
>
> #include <linux/array_size.h>
> +#include <linux/bitfield.h>
> #include <linux/device.h>
> #include <linux/err.h>
> #include <linux/i2c.h>
> #include <linux/mfd/adp5585.h>
> #include <linux/mfd/core.h>
> +#include <linux/minmax.h>
> #include <linux/mod_devicetable.h>
> #include <linux/module.h>
> #include <linux/regmap.h>
> @@ -205,11 +207,19 @@ static const struct adp5585_regs adp5585_regs = {
> .gpo_out_a = ADP5585_GPO_OUT_MODE_A,
> .gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
> .gpi_stat_a = ADP5585_GPI_STATUS_A,
> + .gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
> + .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
> .pwm_cfg = ADP5585_PWM_CFG,
> .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> + .reset_cfg = ADP5585_RESET_CFG,
> .gen_cfg = ADP5585_GENERAL_CFG,
> .ext_cfg = ADP5585_PIN_CONFIG_C,
> + .pin_cfg_a = ADP5585_PIN_CONFIG_A,
> + .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> + .int_en = ADP5585_INT_EN,
> + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> };
>
> static const struct adp5585_regs adp5589_regs = {
> @@ -219,11 +229,19 @@ static const struct adp5585_regs adp5589_regs = {
> .gpo_out_a = ADP5589_GPO_OUT_MODE_A,
> .gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
> .gpi_stat_a = ADP5589_GPI_STATUS_A,
> + .gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
> + .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
> .pwm_cfg = ADP5589_PWM_CFG,
> .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> + .reset_cfg = ADP5589_RESET_CFG,
> .gen_cfg = ADP5589_GENERAL_CFG,
> .ext_cfg = ADP5589_PIN_CONFIG_D,
> + .pin_cfg_a = ADP5589_PIN_CONFIG_A,
> + .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> + .int_en = ADP5589_INT_EN,
> + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> };
>
> static const struct adp5585_info adp5585_info = {
> @@ -234,6 +252,8 @@ static const struct adp5585_info adp5585_info = {
> .regs = &adp5585_regs,
> .max_rows = ADP5585_MAX_ROW_NUM,
> .max_cols = ADP5585_MAX_COL_NUM,
> + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5585_01_info = {
> @@ -241,9 +261,12 @@ static const struct adp5585_info adp5585_01_info = {
> .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> .n_devs = ARRAY_SIZE(adp5585_devs),
> .id = ADP5585_MAN_ID_VALUE,
> + .has_row5 = true,
> .regs = &adp5585_regs,
> .max_rows = ADP5585_MAX_ROW_NUM,
> .max_cols = ADP5585_MAX_COL_NUM,
> + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5585_02_info = {
> @@ -254,6 +277,8 @@ static const struct adp5585_info adp5585_02_info = {
> .regs = &adp5585_regs,
> .max_rows = ADP5585_MAX_ROW_NUM,
> .max_cols = ADP5585_MAX_COL_NUM,
> + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5585_04_info = {
> @@ -264,6 +289,8 @@ static const struct adp5585_info adp5585_04_info = {
> .regs = &adp5585_regs,
> .max_rows = ADP5585_MAX_ROW_NUM,
> .max_cols = ADP5585_MAX_COL_NUM,
> + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5589_info = {
> @@ -271,9 +298,13 @@ static const struct adp5585_info adp5589_info = {
> .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> .n_devs = ARRAY_SIZE(adp5589_devs),
> .id = ADP5589_MAN_ID_VALUE,
> + .has_row5 = true,
> + .has_unlock = true,
> .regs = &adp5589_regs,
> .max_rows = ADP5589_MAX_ROW_NUM,
> .max_cols = ADP5589_MAX_COL_NUM,
> + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5589_01_info = {
> @@ -281,9 +312,13 @@ static const struct adp5585_info adp5589_01_info = {
> .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> .n_devs = ARRAY_SIZE(adp5589_devs),
> .id = ADP5589_MAN_ID_VALUE,
> + .has_row5 = true,
> + .has_unlock = true,
> .regs = &adp5589_regs,
> .max_rows = ADP5589_MAX_ROW_NUM,
> .max_cols = ADP5589_MAX_COL_NUM,
> + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> };
>
> static const struct adp5585_info adp5589_02_info = {
> @@ -291,11 +326,379 @@ static const struct adp5585_info adp5589_02_info = {
> .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> .n_devs = ARRAY_SIZE(adp5589_devs),
> .id = ADP5589_MAN_ID_VALUE,
> + .has_row5 = true,
> + .has_unlock = true,
> .regs = &adp5589_regs,
> .max_rows = ADP5589_MAX_ROW_NUM,
> .max_cols = ADP5589_MAX_COL_NUM,
> + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> };
>
> +static int adp5585_keys_validate_key(const struct adp5585_dev *adp5585, u32 key,
> + bool is_gpi)
> +{
> + const struct adp5585_info *info = adp5585->info;
> + struct device *dev = adp5585->dev;
> + u32 row, col;
> +
> + if (is_gpi) {
> + u32 gpi = key - adp5585->info->gpi_ev_base;
> +
> + if (!info->has_row5 && gpi == ADP5585_ROW5)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid unlock/reset GPI(%u) not supported\n",
> + gpi);
> +
> + /* check if it's being used in the keypad */
> + if (test_bit(gpi, adp5585->keypad))
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid unlock/reset GPI(%u) being used in the keypad\n",
> + gpi);
> +
> + return 0;
> + }
> +
> + row = (key - 1) / info->max_cols;
> + col = (key - 1) % info->max_cols;
> +
> + /* both the row and col must be part of the keypad */
> + if (test_bit(row, adp5585->keypad) &&
> + test_bit(col + info->max_rows, adp5585->keypad))
> + return 0;
> +
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid unlock/reset key(%u) not used in the keypad\n", key);
> +}
> +
> +static int adp5585_keys_parse_array(const struct adp5585_dev *adp5585,
> + const char *prop, u32 *keys, u32 *n_keys,
> + u32 max_keys, bool reset_key)
> +{
> + const struct adp5585_info *info = adp5585->info;
> + struct device *dev = adp5585->dev;
> + unsigned int key, max_keypad;
> + int ret;
> +
> + ret = device_property_count_u32(dev, prop);
> + if (ret < 0)
> + return 0;
> +
> + *n_keys = ret;
> +
> + if (!info->has_unlock && !reset_key)
> + return dev_err_probe(dev, -EOPNOTSUPP,
> + "Unlock keys not supported\n");
> +
> + if (*n_keys > max_keys)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid number of keys(%u > %u) for %s\n",
> + *n_keys, max_keys, prop);
> +
> + ret = device_property_read_u32_array(dev, prop, keys, *n_keys);
> + if (ret)
> + return ret;
> +
> + max_keypad = adp5585->info->max_rows * adp5585->info->max_cols;
> +
> + for (key = 0; key < *n_keys; key++) {
> + /* part of the keypad... */
> + if (in_range(keys[key], 1, max_keypad)) {
> + /* is it part of the keypad?! */
> + ret = adp5585_keys_validate_key(adp5585, keys[key],
> + false);
> + if (ret)
> + return ret;
> +
> + continue;
> + }
> +
> + /* part of gpio-keys... */
> + if (in_range(keys[key], adp5585->info->gpi_ev_base,
> + info->max_cols + info->max_rows)) {
> + /* is the GPI being used as part of the keypad?! */
> + ret = adp5585_keys_validate_key(adp5585, keys[key],
> + true);
> + if (ret)
> + return ret;
> +
> + continue;
> + }
> +
> + if (!reset_key && keys[key] == 127)
> + continue;
> +
> + return dev_err_probe(dev, -EINVAL, "Invalid key(%u) for %s\n",
> + keys[key], prop);
> + }
> +
> + return 0;
> +}
> +
> +static int adp5585_keys_unlock_parse(struct adp5585_dev *adp5585)
> +{
> + struct device *dev = adp5585->dev;
> + int ret;
> +
> + ret = adp5585_keys_parse_array(adp5585, "adi,unlock-keys",
> + adp5585->unlock_keys,
> + &adp5585->nkeys_unlock,
> + ARRAY_SIZE(adp5585->unlock_keys), false);
> + if (ret)
> + return ret;
> + if (!adp5585->nkeys_unlock)
> + /* no unlock keys */
> + 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_keys_reset_parse(struct adp5585_dev *adp5585)
> +{
> + const struct adp5585_info *info = adp5585->info;
> + struct device *dev = adp5585->dev;
> + u32 prop_val;
> + int ret;
> +
> + ret = adp5585_keys_parse_array(adp5585, "adi,reset1-keys",
> + adp5585->reset1_keys,
> + &adp5585->nkeys_reset1,
> + ARRAY_SIZE(adp5585->reset1_keys), true);
> + if (ret)
> + return ret;
> +
> + if (adp5585->nkeys_reset1 > 0) {
> + if (test_bit(ADP5585_ROW4, adp5585->keypad))
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid reset1 output(R4) being used in the keypad\n");
> +
> + if (device_property_read_bool(dev, "adi,reset1-active-high"))
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
> + }
> +
> + ret = adp5585_keys_parse_array(adp5585, "adi,reset2-keys",
> + adp5585->reset2_keys,
> + &adp5585->nkeys_reset2,
> + ARRAY_SIZE(adp5585->reset2_keys), true);
> + if (ret)
> + return ret;
> +
> + if (adp5585->nkeys_reset2 > 0) {
> + if (test_bit(info->max_rows + ADP5585_COL4, adp5585->keypad))
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid reset2 output(C4) being used in the keypad\n");
> +
> + if (device_property_read_bool(dev, "adi,reset2-active-high"))
> + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
> + }
> +
> + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
> + return 0;
> +
> + if (device_property_read_bool(dev, "adi,rst-passtrough-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)
> +{
> + const struct adp5585_info *info = adp5585->info;
> + unsigned int n_pins = info->max_cols + info->max_rows;
> + unsigned int *keypad_pins;
> + unsigned int prop_val;
> + int n_keys, key, ret;
> +
> + adp5585->keypad = devm_bitmap_zalloc(dev, n_pins, GFP_KERNEL);
> + if (!adp5585->keypad)
> + return -ENOMEM;
> +
> + if (device_property_present(dev, "#pwm-cells"))
> + adp5585->has_pwm = true;
> +
> + n_keys = device_property_count_u32(dev, "adi,keypad-pins");
> + if (n_keys <= 0)
> + goto no_keypad;
> + if (n_keys > n_pins)
> + return -EINVAL;
> +
> + keypad_pins = devm_kcalloc(dev, n_keys, sizeof(*keypad_pins),
> + GFP_KERNEL);
> + if (!keypad_pins)
> + return -ENOMEM;
> +
> + ret = device_property_read_u32_array(dev, "adi,keypad-pins",
> + keypad_pins, n_keys);
> + if (ret)
> + return ret;
> +
> + for (key = 0; key < n_keys; key++) {
> + if (keypad_pins[key] >= n_pins)
> + return -EINVAL;
> + if (adp5585->has_pwm && keypad_pins[key] == ADP5585_ROW3)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid PWM pin being used in the keypad\n");
> + if (!info->has_row5 && keypad_pins[key] == ADP5585_ROW5)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid row5 being used in the keypad\n");
> + __set_bit(keypad_pins[key], adp5585->keypad);
> + }
> +
> +no_keypad:
> + ret = device_property_read_u32(dev, "adi,key-poll-ms", &prop_val);
> + if (!ret) {
> + switch (prop_val) {
> + case 10:
> + fallthrough;
> + case 20:
> + fallthrough;
> + case 30:
> + fallthrough;
> + case 40:
> + adp5585->key_poll_time = prop_val / 10 - 1;
> + break;
> + default:
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid value(%u) for adi,key-poll-ms\n",
> + prop_val);
> + }
> + }
> +
> + ret = adp5585_keys_unlock_parse(adp5585);
> + if (ret)
> + return ret;
> +
> + return adp5585_keys_reset_parse(adp5585);
> +}
> +
> +static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
> +{
> + unsigned int i;
> +
> + guard(mutex)(&adp5585->ev_lock);
> +
> + 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);
> +
> + if (key_val >= adp5585->info->gpi_ev_base &&
> + key_val <= adp5585->info->gpi_ev_end) {
> + unsigned int gpi = key_val - adp5585->info->gpi_ev_base;
> +
> + if (adp5585->gpio_irq_handle)
> + adp5585->gpio_irq_handle(adp5585->gpio_dev, gpi,
> + key_press);
> + } else if (adp5585->keys_irq_handle) {
> + adp5585->keys_irq_handle(adp5585->input_dev, key_val,
> + key_press);
> + }
> + }
> +}
> +
> +static irqreturn_t adp5585_irq(int irq, void *data)
> +{
> + struct adp5585_dev *adp5585 = data;
> + unsigned int status, ev_cnt;
> + int ret;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
> + if (ret)
> + return IRQ_HANDLED;
> +
> + if (status & ADP5585_OVRFLOW_INT)
> + dev_err_ratelimited(adp5585->dev, "Event Overflow Error\n");
> +
> + if (!(status & ADP5585_EVENT_INT))
> + goto out_irq;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
> + if (ret)
> + goto out_irq;
> +
> + ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
> + if (!ev_cnt)
> + goto out_irq;
> +
> + adp5585_report_events(adp5585, ev_cnt);
> +out_irq:
> + regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
> + return IRQ_HANDLED;
> +}
> +
> static void adp5585_osc_disable(void *data)
> {
> const struct adp5585_dev *adp5585 = data;
> @@ -303,6 +706,128 @@ static void adp5585_osc_disable(void *data)
> regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> }
>
> +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->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,
> + ®_val);
> + if (ret)
> + return ret;
> + }
> +
> + ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> + adp5585->key_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)
> {
> const struct adp5585_info *info;
> @@ -321,6 +846,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return -ENODEV;
>
> adp5585->info = info;
> + adp5585->dev = &i2c->dev;
> + adp5585->irq = i2c->irq;
>
> adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> if (IS_ERR(adp5585->regmap))
> @@ -337,12 +864,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> return dev_err_probe(&i2c->dev, -ENODEV,
> "Invalid device ID 0x%02x\n", id);
>
> - ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> - ADP5585_OSC_EN);
> + ret = adp5585_parse_fw(&i2c->dev, adp5585);
> if (ret)
> return ret;
>
> - ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
> + ret = adp5585_setup(adp5585);
> + if (ret)
> + return ret;
> +
> + ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
> if (ret)
> return ret;
>
> @@ -353,13 +883,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;
> @@ -368,11 +901,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 dffe1449de01dacf8fe78cf0e87d1f176d11f620..3ec542bed9c1c44899cc869d957557813e2d0aab 100644
> --- a/include/linux/mfd/adp5585.h
> +++ b/include/linux/mfd/adp5585.h
> @@ -10,13 +10,21 @@
> #define __MFD_ADP5585_H_
>
> #include <linux/bits.h>
> +#include <linux/cleanup.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 +40,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
> @@ -60,6 +69,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
> @@ -104,8 +114,17 @@
> #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
>
> +#define ADP5585_ROW3 3
> +#define ADP5585_ROW4 4
> +#define ADP5585_ROW5 5
> +#define ADP5585_COL4 4
> +#define ADP5585_MAX_UNLOCK_TIME_SEC 7
> +#define ADP5585_GPI_EVENT_START 37
> +#define ADP5585_GPI_EVENT_END 47
> #define ADP5585_MAX_ROW_NUM 6
> #define ADP5585_MAX_COL_NUM 5
>
> @@ -124,18 +143,38 @@
> #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
> #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
> +#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
> #define ADP5589_MAX_REG ADP5589_INT_EN
>
> +#define ADP5589_GPI_EVENT_START 97
> +#define ADP5589_GPI_EVENT_END 115
> #define ADP5589_MAX_ROW_NUM 8
> #define ADP5589_MAX_COL_NUM 11
>
> @@ -154,11 +193,19 @@ struct adp5585_regs {
> unsigned int gpo_out_a;
> unsigned int gpio_dir_a;
> unsigned int gpi_stat_a;
> + unsigned int gpi_ev_a;
> + unsigned int gpi_int_lvl_a;
> unsigned int pwm_cfg;
> unsigned int pwm_offt_low;
> unsigned int pwm_ont_low;
> + unsigned int reset_cfg;
> unsigned int gen_cfg;
> unsigned int ext_cfg;
> + unsigned int pin_cfg_a;
> + unsigned int poll_ptime_cfg;
> + unsigned int int_en;
> + unsigned int reset1_event_a;
> + unsigned int reset2_event_a;
> };
>
> struct adp5585_info {
> @@ -169,6 +216,10 @@ struct adp5585_info {
> unsigned int id;
> u8 max_rows;
> u8 max_cols;
> + u8 gpi_ev_base;
> + u8 gpi_ev_end;
> + bool has_row5;
> + bool has_unlock;
> };
>
> struct regmap;
> @@ -176,6 +227,51 @@ struct regmap;
> struct adp5585_dev {
> struct regmap *regmap;
> const struct adp5585_info *info;
> + struct device *dev;
> + unsigned long *keypad;
> + void (*gpio_irq_handle)(struct device *dev, unsigned int off,
> + bool key_press);
> + struct device *gpio_dev;
> + void (*keys_irq_handle)(struct device *dev, unsigned int off,
> + bool key_press);
> + struct device *input_dev;
> + /*
> + * Used to synchronize usage (and availability) of gpio_irq_handle()
> + * and keys_irq_handle().
> + */
> + struct mutex ev_lock;
> + int irq;
> + u32 key_poll_time;
> + u32 unlock_time;
> + u32 unlock_keys[2];
> + u32 nkeys_unlock;
> + u32 reset1_keys[3];
> + u32 nkeys_reset1;
> + u32 reset2_keys[2];
> + u32 nkeys_reset2;
> + u8 reset_cfg;
> + bool has_pwm;
> };
>
> +static inline void adp5585_gpio_ev_handle_set(struct adp5585_dev *adp5585,
> + void (*handle)(struct device *dev,
> + unsigned int off,
> + bool key_press),
> + struct device *gpio_dev)
> +{
> + guard(mutex)(&adp5585->ev_lock);
> + adp5585->gpio_irq_handle = handle;
> + adp5585->gpio_dev = gpio_dev;
> +}
> +
> +static inline void adp5585_keys_ev_handle_set(struct adp5585_dev *adp5585,
> + void (*handle)(struct device *dev,
> + unsigned int off,
> + bool key_press),
> + struct device *input_dev)
> +{
> + guard(mutex)(&adp5585->ev_lock);
> + adp5585->keys_irq_handle = handle;
> + adp5585->input_dev = input_dev;
> +}
> #endif
>
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
2025-04-15 14:49 ` [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
2025-04-19 2:44 ` Dmitry Torokhov
@ 2025-04-21 9:35 ` Laurent Pinchart
2025-04-21 12:33 ` Nuno Sá
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:35 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 Tue, Apr 15, 2025 at 03:49:28PM +0100, Nuno Sá via B4 Relay wrote:
> 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 | 221 ++++++++++++++++++++++++++++++++++
> 4 files changed, 234 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b5acf50fc6af4322dec0dad2169b46c6a1903e3c..48bd39a1a96d9c57145cf2560eec54248427fc89 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -549,6 +549,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/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..93961a9e822f8b10b1bca526b9486eed4ad7f8f7
> --- /dev/null
> +++ b/drivers/input/keyboard/adp5585-keys.c
> @@ -0,0 +1,221 @@
> +// 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 {
> + struct input_dev *input;
> + unsigned short keycode[ADP5589_MAX_KEYMAPSIZE];
> + struct device *dev;
> + int row_shift;
> + u8 max_rows;
> + u8 max_cols;
> +};
> +
> +static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
> + struct adp5585_kpad *kpad)
> +{
> + unsigned long row_map, col_map;
> + struct device *dev = kpad->dev;
> + u32 cols = 0, rows = 0;
> + int ret;
> +
> + row_map = bitmap_read(adp5585->keypad, 0, kpad->max_rows);
> + col_map = bitmap_read(adp5585->keypad, kpad->max_rows, kpad->max_cols);
> + /*
> + * 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.
> + */
> + if (!bitmap_empty(&row_map, kpad->max_rows))
> + rows = find_last_bit(&row_map, kpad->max_rows) + 1;
> + if (!bitmap_empty(&col_map, kpad->max_cols))
> + cols = find_last_bit(&col_map, kpad->max_cols) + 1;
> +
> + if (!rows && !cols)
> + return dev_err_probe(dev, -EINVAL,
> + "No rows or columns defined for the keypad\n");
> +
> + if (cols && !rows)
> + return dev_err_probe(dev, -EINVAL,
> + "Cannot have columns with no rows!\n");
> +
> + if (rows && !cols)
> + return dev_err_probe(dev, -EINVAL,
> + "Cannot have rows with no columns!\n");
> +
> + ret = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
> + kpad->keycode, kpad->input);
> + if (ret)
> + return ret;
> +
> + kpad->row_shift = get_count_order(cols);
> +
> + if (device_property_present(kpad->dev, "autorepeat"))
> + __set_bit(EV_REP, kpad->input->evbit);
> +
> + return 0;
> +}
> +
> +static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
> + struct adp5585_kpad *kpad)
> +{
> + unsigned long keys_bits, start = 0, nbits = kpad->max_rows;
> + const struct adp5585_regs *regs = adp5585->info->regs;
> + unsigned int i = 0, max_cols = kpad->max_cols;
> + int ret;
> +
> + /*
> + * 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(adp5585->keypad, start, nbits);
> + if (keys_bits) {
> + ret = regmap_write(adp5585->regmap, regs->pin_cfg_a + i,
> + keys_bits);
> + if (ret)
> + return ret;
> + }
> +
> + start += nbits;
> + if (max_cols > 8) {
> + nbits = 8;
> + max_cols -= nbits;
> + } else {
> + nbits = max_cols;
> + }
> +
> + i++;
> + } while (start < kpad->max_rows + kpad->max_cols);
> +
> + return 0;
> +}
> +
> +static void adp5585_keys_ev_handle(struct device *dev, unsigned int key,
> + bool key_press)
> +{
> + struct adp5585_kpad *kpad = dev_get_drvdata(dev);
> + unsigned int row, col, code;
> +
> + row = (key - 1) / (kpad->max_cols);
> + col = (key - 1) % (kpad->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);
> +}
> +
> +static void adp5585_keys_ev_handle_clean(void *adp5585)
> +{
> + adp5585_keys_ev_handle_set(adp5585, NULL, NULL);
> +}
> +
> +static int adp5585_keys_probe(struct platform_device *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 ret;
> +
> + 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");
This causes the following messages to be printed in the kernel log on a
platform where the keypad feature is not declared in DT:
[ 11.625591] adp5585-keys adp5585-keys.1.auto: error -EINVAL: IRQ is mandatory for the keypad
[ 11.625637] adp5585-keys adp5585-keys.1.auto: probe with driver adp5585-keys failed with error -22
The MFD driver should detect which features are declared, and only
register the corresponding MFD cells.
> +
> + kpad->dev = dev;
> + kpad->max_cols = adp5585->info->max_cols;
> + kpad->max_rows = adp5585->info->max_rows;
> +
> + kpad->input = devm_input_allocate_device(dev);
> + if (!kpad->input)
> + return -ENOMEM;
> +
> + ret = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
> + if (ret)
> + return dev_err_probe(dev, ret, "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->dev.parent = dev;
> +
> + input_set_drvdata(kpad->input, kpad);
> +
> + 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);
> +
> + ret = adp5585_keys_parse_fw(adp5585, kpad);
> + if (ret)
> + return ret;
> +
> + ret = adp5585_keys_setup(adp5585, kpad);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, kpad);
> + adp5585_keys_ev_handle_set(adp5585, adp5585_keys_ev_handle, dev);
> + ret = devm_add_action_or_reset(dev, adp5585_keys_ev_handle_clean,
> + adp5585);
> + if (ret)
> + return ret;
> +
> + return input_register_device(kpad->input);
> +}
> +
> +static const struct platform_device_id adp5585_keys_id_table[] = {
> + { "adp5585-keys" },
> + { "adp5589-keys" },
> + { }
> +};
> +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");
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio
2025-04-15 14:49 ` [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
@ 2025-04-21 9:36 ` Laurent Pinchart
0 siblings, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:36 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, Krzysztof Kozlowski
Hi Nuno,
Thank you for the patch.
On Tue, Apr 15, 2025 at 03:49:31PM +0100, Nuno Sá via B4 Relay wrote:
> 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>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.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 b7c36e2d4bfc4e75c856a0b8b9c842627bf3b1b2..218d6d6399d02adf678acff67b4ee246d833d209 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-keys: false
> adi,unlock-trigger-sec: false
> gpio-reserved-ranges: false
> + reset-gpios: false
> adi,keypad-pins:
> minItems: 2
> maxItems: 11
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 13/17] Input: adp5589: remove the driver
2025-04-15 14:49 ` [PATCH v2 13/17] Input: adp5589: remove the driver Nuno Sá via B4 Relay
2025-04-19 2:45 ` Dmitry Torokhov
@ 2025-04-21 9:40 ` Laurent Pinchart
2025-04-21 12:34 ` Nuno Sá
1 sibling, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:40 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 Tue, Apr 15, 2025 at 03:49:29PM +0100, Nuno Sá via B4 Relay wrote:
> 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")
Those commit IDs won't exist in mainline. With that fixed,
> is doing.
>
> 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 ---------------------------------
Shouldn't you also drop include/linux/input/adp5589.h ?
With those issues fixed,
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 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");
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 16/17] mfd: adp5585: add support for a reset pin
2025-04-15 14:49 ` [PATCH v2 16/17] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
@ 2025-04-21 9:46 ` Laurent Pinchart
2025-04-21 12:42 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:46 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 Tue, Apr 15, 2025 at 03:49:32PM +0100, 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 | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index 667cc5bd0745f64eec60837ec3c00057af0cddeb..2af110fb28054c8ad6709a9a42cb21919be433e7 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -12,6 +12,7 @@
> #include <linux/device.h>
> #include <linux/err.h>
> #include <linux/i2c.h>
> +#include <linux/gpio/consumer.h>
Alphabetic order please.
> #include <linux/mfd/adp5585.h>
> #include <linux/mfd/core.h>
> #include <linux/minmax.h>
> @@ -833,6 +834,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> {
> const struct adp5585_info *info;
> struct adp5585_dev *adp5585;
> + struct gpio_desc *gpio;
> unsigned int id;
> int ret;
>
> @@ -854,6 +856,16 @@ 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);
> +
> + if (gpio) {
> + fsleep(30);
> + gpiod_set_value_cansleep(gpio, 0);
> + fsleep(60);
Where do those values come from ? The only possibly related timing
information I found in the datasheet indicate a GPIO debounce delay of
70µs.
> + }
> +
> adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator
2025-04-15 14:49 ` [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
@ 2025-04-21 9:48 ` Laurent Pinchart
2025-04-21 12:38 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:48 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 Tue, Apr 15, 2025 at 03:49:30PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Make sure we get and enable the VDD supply (if available).
Can the regulator be enabled only when needed ?
> 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 c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b..667cc5bd0745f64eec60837ec3c00057af0cddeb 100644
> --- a/drivers/mfd/adp5585.c
> +++ b/drivers/mfd/adp5585.c
> @@ -18,6 +18,7 @@
> #include <linux/mod_devicetable.h>
> #include <linux/module.h>
> #include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> #include <linux/types.h>
>
> static const struct mfd_cell adp5585_devs[] = {
> @@ -849,6 +850,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> adp5585->dev = &i2c->dev;
> adp5585->irq = i2c->irq;
>
> + ret = devm_regulator_get_enable(&i2c->dev, "vdd");
> + if (ret)
> + return ret;
> +
> adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> if (IS_ERR(adp5585->regmap))
> return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h
2025-04-15 14:49 ` [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
@ 2025-04-21 9:50 ` Laurent Pinchart
0 siblings, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 9:50 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 Tue, Apr 15, 2025 at 03:49:33PM +0100, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Explicitly include mod_devicetable.h for struct platform_device_id.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.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 cc8ac8f9e5669b4ffca06d4117a29f030393f48f..85308257724a338da4d2416c8d01e48e08bd0856 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>
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-04-15 15:56 ` [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Laurent Pinchart
@ 2025-04-21 10:08 ` Laurent Pinchart
0 siblings, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 10:08 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, Krzysztof Kozlowski
Hi Nuno,
On Tue, Apr 15, 2025 at 06:56:09PM +0300, Laurent Pinchart wrote:
> On Tue, Apr 15, 2025 at 03:49:16PM +0100, Nuno Sá via B4 Relay wrote:
> > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > and PWM. However, the gpio part of it was already supported as part of
> > the keyboard driver:
> >
> > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> >
> > On top of that it also overlapped with my refactoring of the above driver [1]
> > to drop usage of platform data and use FW properties instead.
> >
> > Now, it actually makes sense for this device to be supported under MFD
> > and since the "legacy" input device depends on platform data that is not
> > defined anywhere the plan in this series is to add support for the
> > keyboard and adp5589 devices as part of the MFD driver. Once the MFD
> > driver supports all that's supported in the Input one, we drop it...
> >
> > For DT Maintainers:
> >
> > The compatible for adp5589 is part of trivial devices. To me, it makes
> > sense to remove it in the patch where we drop the driver but doing so
> > would result in a warning when adding the same compatible for the MFD
> > bindings. Hence, I remove it in that patch. Is that ok?
> >
> > Uwe:
> >
> > In my eval board, I could see that reading the GPIO value (when
> > configured as input) does not work when OSC_EN is not set. Therefore,
How did you test that, through the GPI_STATUS_x register ?
> > commit ("pwm: adp5585: don't control OSC_EN in the pwm driver") could
> > very well have a Fixes tag. However I'm not 100% sure it's a real issue
> > or something special to my eval board.
> >
> > It would be nice if Laurent or Liu could test the PWM bits or even
> > check that the above is also an issue for their platform.
>
> I'll give it a try, but it will need to wait until next week.
I can't easily test GPI or PWM with my hardware setup at the moment :-(.
I can however confirm that this series doesn't break GPO support for my
use case.
> > [1]: https://lore.kernel.org/linux-input/d1395bd61ce58b3734121bca4e09605a3e997af3.camel@gmail.com/
> >
> > BTW the series is based on linux-next/master
> >
> > ---
> > Changes in v2:
> > - Patch 5:
> > * Do not nest if:then:else::if:then.
> > - Patch 6:
> > * Make use of the adp5585 info variables and adp5589 volatile regs.
> > - Patch 9:
> > * Use standard "poll-interval" property (and move it before vendor
> > properties).
> > - Patch 10:
> > * Make sure to include bitfield.h.
> >
> > - Link to v1: https://lore.kernel.org/r/20250313-dev-adp5589-fw-v1-0-20e80d4bd4ea@analog.com
> >
> > ---
> > Nuno Sá (17):
> > dt-bindings: mfd: adp5585: ease on the required properties
> > 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: add support for adp5589
> > gpio: adp5585: add support for the ad5589 expander
> > pwm: adp5585: add support for adp5589
> > dt-bindings: mfd: adp5585: add properties for input events
> > mfd: adp5585: add support for key events
> > 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 | 299 +++++-
> > drivers/input/keyboard/Kconfig | 21 +-
> > drivers/input/keyboard/Makefile | 2 +-
> > drivers/input/keyboard/adp5585-keys.c | 221 ++++
> > drivers/input/keyboard/adp5589-keys.c | 1066 --------------------
> > drivers/mfd/adp5585.c | 808 ++++++++++++++-
> > drivers/pwm/pwm-adp5585.c | 57 +-
> > include/linux/mfd/adp5585.h | 153 ++-
> > 12 files changed, 1709 insertions(+), 1162 deletions(-)
> > ---
> > base-commit: 5b37f7bfff3b1582c34be8fb23968b226db71ebd
> > change-id: 20250311-dev-adp5589-fw-e04cfd945286
> > --
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-21 8:56 ` Laurent Pinchart
@ 2025-04-21 12:12 ` Nuno Sá
2025-04-21 12:29 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:12 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 Mon, 2025-04-21 at 11:56 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:17PM +0100, Nuno Sá via B4 Relay wrote:
> > 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.
>
> This seems to make sense.
>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> I however expected changes in this series to *not* register MFD cells
> for the devices not enabled in DT. Could you do so in v3, on top of this
> patch ?
Makes sense... In theory, I would go with MFD_CELL_OF() but that would need (I
guess) bindings for all the devices and since PWM and GPIO were not introduced
with that...
Anyways, I'll look into some "mandatory" property for each of the supported
cells and use that as deciding point.
- Nuno Sá
>
> > 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..e30e22f964f78519b2ec207e9415e4897d
> > b5c702 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:
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe
2025-04-21 8:57 ` Laurent Pinchart
@ 2025-04-21 12:14 ` Nuno Sá
2025-04-21 22:03 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:14 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 Mon, 2025-04-21 at 11:57 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:18PM +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).
>
> Setting this bit unconditionally increases power consumption. It should
> only be set when needed.
Well, not sure if the effort for that pays off... The only usecase were it would
make sense to do that would be for PWM. For the other devices (and assuming I'm
right with the GPI case) we need this always set.
- Nuno Sá
>
> > 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
> > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..f17b5f2474cac6a403556694066f438288
> > 264a49 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -110,6 +110,13 @@ static const struct regmap_config
> > adp5585_regmap_configs[] = {
> > },
> > };
> >
> > +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;
> > @@ -138,6 +145,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return dev_err_probe(&i2c->dev, -ENODEV,
> > "Invalid device ID 0x%02x\n", id);
> >
> > + 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,
> > adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > NULL, 0, NULL);
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-21 9:15 ` Laurent Pinchart
@ 2025-04-21 12:21 ` Nuno Sá
2025-04-21 22:06 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:21 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 Mon, 2025-04-21 at 12:15 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:22PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > programmable logic, reset generator, and PWM generator.
> >
> > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > drivers. Most importantly, we need to differentiate between some
> > registers addresses. It also hints to future keymap support.
>
> Please split this in two patches, one that reworks the driver to support
> different register addresses, and one that adds adp5589 support.
>
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 223
> > +++++++++++++++++++++++++++++++++++++++++---
> > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > 2 files changed, 268 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d94667993410610be7
> > fc3672 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
> >
> > };
> >
> > +static const struct mfd_cell adp5589_devs[] = {
> > + MFD_CELL_NAME("adp5589-keys"),
> > + 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),
> > };
> > @@ -34,6 +41,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:
> > @@ -77,10 +93,52 @@ 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,
> > +};
> > +
> > enum adp5585_regmap_type {
> > ADP5585_REGMAP_00,
> > ADP5585_REGMAP_02,
> > ADP5585_REGMAP_04,
> > + ADP5589_REGMAP_00,
> > + ADP5589_REGMAP_01,
> > + ADP5589_REGMAP_02,
> > };
> >
> > static const struct regmap_config adp5585_regmap_configs[] = {
> > @@ -111,6 +169,131 @@ static const struct regmap_config
> > adp5585_regmap_configs[] = {
> > .reg_defaults_raw = adp5585_regmap_defaults_04,
> > .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> > },
> > + [ADP5589_REGMAP_00] = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ADP5589_MAX_REG,
> > + .volatile_table = &adp5589_volatile_regs,
> > + .cache_type = REGCACHE_MAPLE,
> > + .reg_defaults_raw = adp5589_regmap_defaults_00,
> > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_00),
> > + },
> > + [ADP5589_REGMAP_01] = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ADP5589_MAX_REG,
> > + .volatile_table = &adp5589_volatile_regs,
> > + .cache_type = REGCACHE_MAPLE,
> > + .reg_defaults_raw = adp5589_regmap_defaults_01,
> > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_01),
> > + },
> > + [ADP5589_REGMAP_02] = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ADP5589_MAX_REG,
> > + .volatile_table = &adp5589_volatile_regs,
> > + .cache_type = REGCACHE_MAPLE,
> > + .reg_defaults_raw = adp5589_regmap_defaults_02,
> > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_02),
> > + },
> > +};
> > +
> > +static const struct adp5585_regs adp5585_regs = {
> > + .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,
> > + .pwm_cfg = ADP5585_PWM_CFG,
> > + .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > + .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > + .gen_cfg = ADP5585_GENERAL_CFG,
> > + .ext_cfg = ADP5585_PIN_CONFIG_C,
> > +};
>
> Why does this need to be stored in this driver, and not in the drivers
> for the gpio and pwm cells ? If the kernel is compiled without e.g. the
> adp5585-pwm driver, we shouldn't waste memory here by adding data that
> only the adp5585-pwm driver needs.
>
I don't really think the memory we would save to be that relevant but I can
better separate things. I guess i went like this because there's some shared
variables that will have to be in the top level structs and I did not wanted to
have a "global" and "local" regs thingy...
- Nuno Sá
> > +
> > +static const struct adp5585_regs adp5589_regs = {
> > + .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,
> > + .pwm_cfg = ADP5589_PWM_CFG,
> > + .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> > + .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> > + .gen_cfg = ADP5589_GENERAL_CFG,
> > + .ext_cfg = ADP5589_PIN_CONFIG_D,
> > +};
> > +
> > +static const struct adp5585_info adp5585_info = {
> > + .adp5585_devs = adp5585_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > + .id = ADP5585_MAN_ID_VALUE,
> > + .regs = &adp5585_regs,
> > + .max_rows = ADP5585_MAX_ROW_NUM,
> > + .max_cols = ADP5585_MAX_COL_NUM,
>
> Same here, the max_rows and max_cols fields don't seem to belong to this
> driver.
>
> > +};
> > +
> > +static const struct adp5585_info adp5585_01_info = {
> > + .adp5585_devs = adp5585_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > + .id = ADP5585_MAN_ID_VALUE,
> > + .regs = &adp5585_regs,
> > + .max_rows = ADP5585_MAX_ROW_NUM,
> > + .max_cols = ADP5585_MAX_COL_NUM,
> > +};
> > +
> > +static const struct adp5585_info adp5585_02_info = {
> > + .adp5585_devs = adp5585_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > + .id = ADP5585_MAN_ID_VALUE,
> > + .regs = &adp5585_regs,
> > + .max_rows = ADP5585_MAX_ROW_NUM,
> > + .max_cols = ADP5585_MAX_COL_NUM,
> > +};
> > +
> > +static const struct adp5585_info adp5585_04_info = {
> > + .adp5585_devs = adp5585_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > + .id = ADP5585_MAN_ID_VALUE,
> > + .regs = &adp5585_regs,
> > + .max_rows = ADP5585_MAX_ROW_NUM,
> > + .max_cols = ADP5585_MAX_COL_NUM,
> > +};
> > +
> > +static const struct adp5585_info adp5589_info = {
> > + .adp5585_devs = adp5589_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > + .id = ADP5589_MAN_ID_VALUE,
> > + .regs = &adp5589_regs,
> > + .max_rows = ADP5589_MAX_ROW_NUM,
> > + .max_cols = ADP5589_MAX_COL_NUM,
> > +};
> > +
> > +static const struct adp5585_info adp5589_01_info = {
> > + .adp5585_devs = adp5589_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > + .id = ADP5589_MAN_ID_VALUE,
> > + .regs = &adp5589_regs,
> > + .max_rows = ADP5589_MAX_ROW_NUM,
> > + .max_cols = ADP5589_MAX_COL_NUM,
> > +};
> > +
> > +static const struct adp5585_info adp5589_02_info = {
> > + .adp5585_devs = adp5589_devs,
> > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > + .id = ADP5589_MAN_ID_VALUE,
> > + .regs = &adp5589_regs,
> > + .max_rows = ADP5589_MAX_ROW_NUM,
> > + .max_cols = ADP5589_MAX_COL_NUM,
> > };
> >
> > static void adp5585_osc_disable(void *data)
> > @@ -122,7 +305,7 @@ static void adp5585_osc_disable(void *data)
> >
> > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > {
> > - const struct regmap_config *regmap_config;
> > + const struct adp5585_info *info;
> > struct adp5585_dev *adp5585;
> > unsigned int id;
> > int ret;
> > @@ -133,8 +316,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> >
> > i2c_set_clientdata(i2c, adp5585);
> >
> > - regmap_config = i2c_get_match_data(i2c);
> > - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> > + info = i2c_get_match_data(i2c);
> > + if (!info)
> > + return -ENODEV;
>
> Can this happen ?
>
> > +
> > + adp5585->info = info;
>
> Drop the local info variable and assign the value to adp5585->info
> directly.
>
> > +
> > + adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > if (IS_ERR(adp5585->regmap))
> > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > "Failed to initialize register
> > map\n");
> > @@ -144,7 +332,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);
> >
> > @@ -158,8 +347,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return ret;
> >
> > ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > - adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > - NULL, 0, NULL);
> > + adp5585->info->adp5585_devs,
> > + adp5585->info->n_devs, NULL, 0, NULL);
> > if (ret)
> > return dev_err_probe(&i2c->dev, ret,
> > "Failed to add child devices\n");
> > @@ -191,19 +380,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm,
> > adp5585_suspend, adp5585_resume);
> > static const struct of_device_id adp5585_of_match[] = {
> > {
> > .compatible = "adi,adp5585-00",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .data = &adp5585_info,
> > }, {
> > .compatible = "adi,adp5585-01",
> > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > + .data = &adp5585_01_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,
> > + }, {
> > + .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
> > 016033cd68e46757aca86d21dd37025fd354b801..dffe1449de01dacf8fe78cf0e87d1f176d
> > 11f620 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -104,9 +104,11 @@
> > #define ADP5585_INT_CFG BIT(1)
> > #define ADP5585_RST_CFG BIT(0)
> > #define ADP5585_INT_EN 0x3c
> > -
> > #define ADP5585_MAX_REG ADP5585_INT_EN
> >
> > +#define ADP5585_MAX_ROW_NUM 6
> > +#define ADP5585_MAX_COL_NUM 5
> > +
> > /*
> > * 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
> > @@ -117,10 +119,63 @@
> > #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_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
>
> Indentation looks wrong.
>
> > +#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_GENERAL_CFG 0x4d
> > +#define ADP5589_INT_EN 0x4e
> > +#define ADP5589_MAX_REG ADP5589_INT_EN
> > +
> > +#define ADP5589_MAX_ROW_NUM 8
> > +#define ADP5589_MAX_COL_NUM 11
> > +
> > +/*
> > + * 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_regs {
> > + 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 pwm_cfg;
> > + unsigned int pwm_offt_low;
> > + unsigned int pwm_ont_low;
> > + unsigned int gen_cfg;
> > + unsigned int ext_cfg;
> > +};
> > +
> > +struct adp5585_info {
> > + const struct mfd_cell *adp5585_devs;
> > + const struct regmap_config *regmap_config;
> > + const struct adp5585_regs *regs;
> > + unsigned int n_devs;
> > + unsigned int id;
> > + u8 max_rows;
> > + u8 max_cols;
> > +};
> > +
> > struct regmap;
> >
> > struct adp5585_dev {
> > struct regmap *regmap;
> > + const struct adp5585_info *info;
> > };
> >
> > #endif
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander
2025-04-21 9:23 ` Laurent Pinchart
@ 2025-04-21 12:22 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:22 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 Mon, 2025-04-21 at 12:23 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:23PM +0100, Nuno Sá via B4 Relay wrote:
> > 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.
>
> Same comment as for 06/17, splitting the patch in two will make it
> easier to review.
>
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/gpio/gpio-adp5585.c | 121 ++++++++++++++++++++++++++++++++---------
> > ---
> > 1 file changed, 88 insertions(+), 33 deletions(-)
> >
> > diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
> > index
> > d5c0f1b267c82a5002b50cbb7a108166439e4785..d8f8d5513d7f6a9acf5bdecccacc89c461
> > 5ce237 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,21 +15,49 @@
> > #include <linux/regmap.h>
> > #include <linux/types.h>
> >
> > -#define ADP5585_GPIO_MAX 11
> > +struct adp5585_gpio_chip {
> > + unsigned int max_gpio;
> > + int (*bank)(unsigned int off);
> > + int (*bit)(unsigned int off);
> > + bool has_bias_hole;
> > +};
> >
> > struct adp5585_gpio_dev {
> > struct gpio_chip gpio_chip;
> > + const struct adp5585_gpio_chip *info;
> > struct regmap *regmap;
> > + const struct adp5585_regs *regs;
> > };
> >
> > +static int adp5585_gpio_bank(unsigned int off)
> > +{
> > + return ADP5585_BANK(off);
>
> While at it, let's move the ADP558[59]_{BANK,BIT} macros to this file,
> and name them ADP558[59]_GPIO_{BANK_BIT}.
Sure...
>
> > +}
> > +
> > +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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->info->bit(off);
> > unsigned int val;
> >
> > - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank,
> > &val);
> > + regmap_read(adp5585_gpio->regmap, regs->gpio_dir_a + bank, &val);
> >
> > return val & bit ? GPIO_LINE_DIRECTION_OUT :
> > GPIO_LINE_DIRECTION_IN;
> > }
> > @@ -36,35 +65,37 @@ static int adp5585_gpio_get_direction(struct gpio_chip
> > *chip, unsigned int off)
> > 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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->info->bit(off);
> >
> > - return regmap_clear_bits(adp5585_gpio->regmap,
> > - ADP5585_GPIO_DIRECTION_A + bank, bit);
> > + return regmap_clear_bits(adp5585_gpio->regmap, regs->gpio_dir_a +
> > bank,
> > + bit);
> > }
> >
> > 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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->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, regs->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, regs->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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->info->bit(off);
> > unsigned int reg;
> > unsigned int val;
> >
> > @@ -79,8 +110,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, regs->gpio_dir_a + bank, &val);
> > + reg = val & bit ? regs->gpo_data_a : regs->gpi_stat_a;
> > regmap_read(adp5585_gpio->regmap, reg + bank, &val);
> >
> > return !!(val & bit);
> > @@ -90,17 +121,19 @@ 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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + 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, regs->gpo_data_a +
> > bank,
> > 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;
> > + const struct adp5585_regs *regs = adp5585_gpio->regs;
> > unsigned int bit, reg, mask, val;
> >
> > /*
> > @@ -108,8 +141,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 = regs->rpull_cfg_a + bit / 8;
> > mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
> > val = bias << (bit % 8);
> >
> > @@ -119,22 +154,24 @@ 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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->info->bit(off);
> >
> > return regmap_update_bits(adp5585_gpio->regmap,
> > - ADP5585_GPO_OUT_MODE_A + bank, bit,
> > + regs->gpo_out_a + bank, 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_regs *regs = adp5585_gpio->regs;
> > + unsigned int bank = adp5585_gpio->info->bank(off);
> > + unsigned int bit = adp5585_gpio->info->bit(off);
> >
> > return regmap_update_bits(adp5585_gpio->regmap,
> > - ADP5585_DEBOUNCE_DIS_A + bank, bit,
> > + regs->debounce_dis_a + bank, bit,
> > debounce ? 0 : bit);
> > }
> >
> > @@ -175,6 +212,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;
> > @@ -185,6 +223,11 @@ static int adp5585_gpio_probe(struct platform_device
> > *pdev)
> > return -ENOMEM;
> >
> > adp5585_gpio->regmap = adp5585->regmap;
> > + adp5585_gpio->regs = adp5585->info->regs;
> > +
> > + 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);
> >
> > @@ -199,7 +242,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->info->max_cols + adp5585->info->max_rows;
> > gc->label = pdev->name;
> > gc->owner = THIS_MODULE;
> >
> > @@ -211,8 +254,20 @@ 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,
> > + .has_bias_hole = true,
> > +};
> > +
> > +static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
> > + .bank = adp5589_gpio_bank,
> > + .bit = adp5589_gpio_bit,
> > +};
> > +
> > 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);
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events
2025-04-21 9:28 ` Laurent Pinchart
@ 2025-04-21 12:24 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:24 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 Mon, 2025-04-21 at 12:28 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:25PM +0100, Nuno Sá via B4 Relay wrote:
> > 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.
> >
> > 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
> > d690a411b3a4307f6dd5f4a432a357e89fefabb0..b7c36e2d4bfc4e75c856a0b8b9c842627b
> > f3b1b2 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-keys:
> > + description:
> > + Specifies a maximum of 2 keys that can be used to unlock the keypad.
> > + If this property is set, the keyboard will be locked and only
> > unlocked
> > + after these keys are pressed. The value 127 serves as a wildcard
> > which
> > + means any key can be used for unlocking.
>
> Is this really something that should be set through DT ? Aren't there
> use cases for configuring this from userspace ? Same for some of the
> other properties below.
I think so. We already have similar properties in other devices like this. I'm
also not sure we have the API for controlling this in userspace. This is also
something that you should likely plan during evaluation of your HW (at least the
reset keys).
- Nuno Sá
>
> > + $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-keys:
> > + description:
> > + Defines the trigger events (key presses) that can generate reset
> > + conditions one the reset1 block.
> > + $ref: /schemas/types.yaml#/definitions/uint32-array
> > + minItems: 1
> > + maxItems: 3
> > +
> > + adi,reset2-keys:
> > + description:
> > + Defines the trigger events (key 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-passtrough-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-keys
> > + adi,reset1-active-high:
> > + - adi,reset1-keys
> > + adi,rst-passtrough-enable:
> > + - adi,reset1-keys
> > + adi,reset2-active-high:
> > + - adi,reset2-keys
> > +
> > 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-keys: false
> > + adi,unlock-trigger-sec: false
> > gpio-reserved-ranges: false
> > -
> > + adi,keypad-pins:
> > + minItems: 2
> > + maxItems: 11
> > + items:
> > + minimum: 0
> > + maximum: 10
> > + adi,reset1-keys:
> > + items:
> > + anyOf:
> > + - minimum: 1
> > + maximum: 30
> > + - minimum: 37
> > + maximum: 47
> > + adi,reset2-keys:
> > + items:
> > + anyOf:
> > + - minimum: 1
> > + maximum: 30
> > + - minimum: 37
> > + maximum: 47
> > - if:
> > properties:
> > compatible:
> > @@ -81,6 +197,25 @@ allOf:
> > - adi,adp5585-02
> > then:
> > properties:
> > + adi,unlock-keys: 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-keys:
> > + items:
> > + anyOf:
> > + - minimum: 1
> > + maximum: 25
> > + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
> > + adi,reset2-keys:
> > + 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-keys:
> > + items:
> > + anyOf:
> > + - minimum: 1
> > + maximum: 88
> > + - minimum: 97
> > + maximum: 115
> > + adi,reset2-keys:
> > + 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-keys = <1 43>;
> > + adi,reset2-keys = <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)
> > + >;
> > };
> > };
> >
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-21 12:12 ` Nuno Sá
@ 2025-04-21 12:29 ` Laurent Pinchart
2025-04-21 12:45 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 12:29 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,
Dmitry Torokhov, Liu Ying
On Mon, Apr 21, 2025 at 01:12:33PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 11:56 +0300, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:17PM +0100, Nuno Sá via B4 Relay wrote:
> > > 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.
> >
> > This seems to make sense.
> >
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> > I however expected changes in this series to *not* register MFD cells
> > for the devices not enabled in DT. Could you do so in v3, on top of this
> > patch ?
>
> Makes sense... In theory, I would go with MFD_CELL_OF() but that would need (I
> guess) bindings for all the devices and since PWM and GPIO were not introduced
> with that...
I initially designed the bindings with child nodes, but got told to
instead use a single DT node :-(
> Anyways, I'll look into some "mandatory" property for each of the supported
> cells and use that as deciding point.
Sounds good to me.
> > > 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..e30e22f964f78519b2ec207e9415e4897d
> > > b5c702 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:
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-21 9:33 ` Laurent Pinchart
@ 2025-04-21 12:32 ` Nuno Sá
2025-04-21 22:13 ` Laurent Pinchart
2025-04-24 16:07 ` Lee Jones
1 sibling, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:32 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 Mon, 2025-04-21 at 12:33 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > This adds support for key events. Basically, it adds support for all the
> > FW parsing and configuration of the keys (also making sure there's no
> > overlaps). They can either be part of a matrix keymap (in which case
> > events will be handled by an input device) or they can be gpi's (in which
> > case events will be handled by the gpiochip device via gpio_keys). There's
> > also support for unlock keys as for reset keys.
>
> There's lots of code below specific to matrix keypad handling. Please
> move it to the keypad driver, and keep this driver as lean as possible.
>
Yeah, I gave this a lot of thought and initially I was doing it in the input
driver (even giving that device it's own of_compatible) but then I went this
way. Basically because unlock and reset keys can also be done through gpio-keys
which is supported in the GPIO driver. Therefore, since these features are
shared between GPIO and INPUT, it made sense to me to put all of this in the top
device. IOW, we can still have these features even if we're not using the input
device.
> > This patch adds all the foundation needed by subsequent changes where the
> > child devices (actually handling the events) can "register" a callback
> > to handle the event.
> >
> > Also to note that enabling the internal oscillator in now done as part of
> > adp5585_setup().
> >
> > We also enable/disable the IRQ (if present) in the corresponding PM
> > event.
>
> The patch does too many things. The base rule should be "one logical
> change, one patch". At the very least the event reporting mechanism
> and the keypad handling should be split in two different patches.
>
Both things are related... Instead maybe we could split things as:
1) Add key handling (which also means DT parsing + device setup + IRQs)
2) Reset keys support
3) Unlock keys support
Thoughts?
- Nuno Sá
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 551
> > +++++++++++++++++++++++++++++++++++++++++++-
> > include/linux/mfd/adp5585.h | 96 ++++++++
> > 2 files changed, 642 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > c3586c0d6aa2e7e7d94667993410610be7fc3672..c1d51d50dca6c9367d4a1b98a4f8bbec12
> > dbf90b 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -8,11 +8,13 @@
> > */
> >
> > #include <linux/array_size.h>
> > +#include <linux/bitfield.h>
> > #include <linux/device.h>
> > #include <linux/err.h>
> > #include <linux/i2c.h>
> > #include <linux/mfd/adp5585.h>
> > #include <linux/mfd/core.h>
> > +#include <linux/minmax.h>
> > #include <linux/mod_devicetable.h>
> > #include <linux/module.h>
> > #include <linux/regmap.h>
> > @@ -205,11 +207,19 @@ static const struct adp5585_regs adp5585_regs = {
> > .gpo_out_a = ADP5585_GPO_OUT_MODE_A,
> > .gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
> > .gpi_stat_a = ADP5585_GPI_STATUS_A,
> > + .gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
> > + .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
> > .pwm_cfg = ADP5585_PWM_CFG,
> > .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > + .reset_cfg = ADP5585_RESET_CFG,
> > .gen_cfg = ADP5585_GENERAL_CFG,
> > .ext_cfg = ADP5585_PIN_CONFIG_C,
> > + .pin_cfg_a = ADP5585_PIN_CONFIG_A,
> > + .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > + .int_en = ADP5585_INT_EN,
> > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > };
> >
> > static const struct adp5585_regs adp5589_regs = {
> > @@ -219,11 +229,19 @@ static const struct adp5585_regs adp5589_regs = {
> > .gpo_out_a = ADP5589_GPO_OUT_MODE_A,
> > .gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
> > .gpi_stat_a = ADP5589_GPI_STATUS_A,
> > + .gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
> > + .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
> > .pwm_cfg = ADP5589_PWM_CFG,
> > .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> > .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> > + .reset_cfg = ADP5589_RESET_CFG,
> > .gen_cfg = ADP5589_GENERAL_CFG,
> > .ext_cfg = ADP5589_PIN_CONFIG_D,
> > + .pin_cfg_a = ADP5589_PIN_CONFIG_A,
> > + .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > + .int_en = ADP5589_INT_EN,
> > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > };
> >
> > static const struct adp5585_info adp5585_info = {
> > @@ -234,6 +252,8 @@ static const struct adp5585_info adp5585_info = {
> > .regs = &adp5585_regs,
> > .max_rows = ADP5585_MAX_ROW_NUM,
> > .max_cols = ADP5585_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5585_01_info = {
> > @@ -241,9 +261,12 @@ static const struct adp5585_info adp5585_01_info = {
> > .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > .n_devs = ARRAY_SIZE(adp5585_devs),
> > .id = ADP5585_MAN_ID_VALUE,
> > + .has_row5 = true,
> > .regs = &adp5585_regs,
> > .max_rows = ADP5585_MAX_ROW_NUM,
> > .max_cols = ADP5585_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5585_02_info = {
> > @@ -254,6 +277,8 @@ static const struct adp5585_info adp5585_02_info = {
> > .regs = &adp5585_regs,
> > .max_rows = ADP5585_MAX_ROW_NUM,
> > .max_cols = ADP5585_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5585_04_info = {
> > @@ -264,6 +289,8 @@ static const struct adp5585_info adp5585_04_info = {
> > .regs = &adp5585_regs,
> > .max_rows = ADP5585_MAX_ROW_NUM,
> > .max_cols = ADP5585_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5589_info = {
> > @@ -271,9 +298,13 @@ static const struct adp5585_info adp5589_info = {
> > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> > .n_devs = ARRAY_SIZE(adp5589_devs),
> > .id = ADP5589_MAN_ID_VALUE,
> > + .has_row5 = true,
> > + .has_unlock = true,
> > .regs = &adp5589_regs,
> > .max_rows = ADP5589_MAX_ROW_NUM,
> > .max_cols = ADP5589_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5589_01_info = {
> > @@ -281,9 +312,13 @@ static const struct adp5585_info adp5589_01_info = {
> > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> > .n_devs = ARRAY_SIZE(adp5589_devs),
> > .id = ADP5589_MAN_ID_VALUE,
> > + .has_row5 = true,
> > + .has_unlock = true,
> > .regs = &adp5589_regs,
> > .max_rows = ADP5589_MAX_ROW_NUM,
> > .max_cols = ADP5589_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > };
> >
> > static const struct adp5585_info adp5589_02_info = {
> > @@ -291,11 +326,379 @@ static const struct adp5585_info adp5589_02_info = {
> > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> > .n_devs = ARRAY_SIZE(adp5589_devs),
> > .id = ADP5589_MAN_ID_VALUE,
> > + .has_row5 = true,
> > + .has_unlock = true,
> > .regs = &adp5589_regs,
> > .max_rows = ADP5589_MAX_ROW_NUM,
> > .max_cols = ADP5589_MAX_COL_NUM,
> > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > };
> >
> > +static int adp5585_keys_validate_key(const struct adp5585_dev *adp5585, u32
> > key,
> > + bool is_gpi)
> > +{
> > + const struct adp5585_info *info = adp5585->info;
> > + struct device *dev = adp5585->dev;
> > + u32 row, col;
> > +
> > + if (is_gpi) {
> > + u32 gpi = key - adp5585->info->gpi_ev_base;
> > +
> > + if (!info->has_row5 && gpi == ADP5585_ROW5)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid unlock/reset GPI(%u)
> > not supported\n",
> > + gpi);
> > +
> > + /* check if it's being used in the keypad */
> > + if (test_bit(gpi, adp5585->keypad))
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid unlock/reset GPI(%u)
> > being used in the keypad\n",
> > + gpi);
> > +
> > + return 0;
> > + }
> > +
> > + row = (key - 1) / info->max_cols;
> > + col = (key - 1) % info->max_cols;
> > +
> > + /* both the row and col must be part of the keypad */
> > + if (test_bit(row, adp5585->keypad) &&
> > + test_bit(col + info->max_rows, adp5585->keypad))
> > + return 0;
> > +
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid unlock/reset key(%u) not used in the
> > keypad\n", key);
> > +}
> > +
> > +static int adp5585_keys_parse_array(const struct adp5585_dev *adp5585,
> > + const char *prop, u32 *keys, u32
> > *n_keys,
> > + u32 max_keys, bool reset_key)
> > +{
> > + const struct adp5585_info *info = adp5585->info;
> > + struct device *dev = adp5585->dev;
> > + unsigned int key, max_keypad;
> > + int ret;
> > +
> > + ret = device_property_count_u32(dev, prop);
> > + if (ret < 0)
> > + return 0;
> > +
> > + *n_keys = ret;
> > +
> > + if (!info->has_unlock && !reset_key)
> > + return dev_err_probe(dev, -EOPNOTSUPP,
> > + "Unlock keys not supported\n");
> > +
> > + if (*n_keys > max_keys)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid number of keys(%u > %u) for
> > %s\n",
> > + *n_keys, max_keys, prop);
> > +
> > + ret = device_property_read_u32_array(dev, prop, keys, *n_keys);
> > + if (ret)
> > + return ret;
> > +
> > + max_keypad = adp5585->info->max_rows * adp5585->info->max_cols;
> > +
> > + for (key = 0; key < *n_keys; key++) {
> > + /* part of the keypad... */
> > + if (in_range(keys[key], 1, max_keypad)) {
> > + /* is it part of the keypad?! */
> > + ret = adp5585_keys_validate_key(adp5585, keys[key],
> > + false);
> > + if (ret)
> > + return ret;
> > +
> > + continue;
> > + }
> > +
> > + /* part of gpio-keys... */
> > + if (in_range(keys[key], adp5585->info->gpi_ev_base,
> > + info->max_cols + info->max_rows)) {
> > + /* is the GPI being used as part of the keypad?! */
> > + ret = adp5585_keys_validate_key(adp5585, keys[key],
> > + true);
> > + if (ret)
> > + return ret;
> > +
> > + continue;
> > + }
> > +
> > + if (!reset_key && keys[key] == 127)
> > + continue;
> > +
> > + return dev_err_probe(dev, -EINVAL, "Invalid key(%u) for
> > %s\n",
> > + keys[key], prop);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int adp5585_keys_unlock_parse(struct adp5585_dev *adp5585)
> > +{
> > + struct device *dev = adp5585->dev;
> > + int ret;
> > +
> > + ret = adp5585_keys_parse_array(adp5585, "adi,unlock-keys",
> > + adp5585->unlock_keys,
> > + &adp5585->nkeys_unlock,
> > + ARRAY_SIZE(adp5585->unlock_keys),
> > false);
> > + if (ret)
> > + return ret;
> > + if (!adp5585->nkeys_unlock)
> > + /* no unlock keys */
> > + 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_keys_reset_parse(struct adp5585_dev *adp5585)
> > +{
> > + const struct adp5585_info *info = adp5585->info;
> > + struct device *dev = adp5585->dev;
> > + u32 prop_val;
> > + int ret;
> > +
> > + ret = adp5585_keys_parse_array(adp5585, "adi,reset1-keys",
> > + adp5585->reset1_keys,
> > + &adp5585->nkeys_reset1,
> > + ARRAY_SIZE(adp5585->reset1_keys),
> > true);
> > + if (ret)
> > + return ret;
> > +
> > + if (adp5585->nkeys_reset1 > 0) {
> > + if (test_bit(ADP5585_ROW4, adp5585->keypad))
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid reset1 output(R4)
> > being used in the keypad\n");
> > +
> > + if (device_property_read_bool(dev, "adi,reset1-active-
> > high"))
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET1_POL, 1);
> > + }
> > +
> > + ret = adp5585_keys_parse_array(adp5585, "adi,reset2-keys",
> > + adp5585->reset2_keys,
> > + &adp5585->nkeys_reset2,
> > + ARRAY_SIZE(adp5585->reset2_keys),
> > true);
> > + if (ret)
> > + return ret;
> > +
> > + if (adp5585->nkeys_reset2 > 0) {
> > + if (test_bit(info->max_rows + ADP5585_COL4, adp5585-
> > >keypad))
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid reset2 output(C4)
> > being used in the keypad\n");
> > +
> > + if (device_property_read_bool(dev, "adi,reset2-active-
> > high"))
> > + adp5585->reset_cfg |=
> > FIELD_PREP(ADP5585_RESET2_POL, 1);
> > + }
> > +
> > + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
> > + return 0;
> > +
> > + if (device_property_read_bool(dev, "adi,rst-passtrough-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)
> > +{
> > + const struct adp5585_info *info = adp5585->info;
> > + unsigned int n_pins = info->max_cols + info->max_rows;
> > + unsigned int *keypad_pins;
> > + unsigned int prop_val;
> > + int n_keys, key, ret;
> > +
> > + adp5585->keypad = devm_bitmap_zalloc(dev, n_pins, GFP_KERNEL);
> > + if (!adp5585->keypad)
> > + return -ENOMEM;
> > +
> > + if (device_property_present(dev, "#pwm-cells"))
> > + adp5585->has_pwm = true;
> > +
> > + n_keys = device_property_count_u32(dev, "adi,keypad-pins");
> > + if (n_keys <= 0)
> > + goto no_keypad;
> > + if (n_keys > n_pins)
> > + return -EINVAL;
> > +
> > + keypad_pins = devm_kcalloc(dev, n_keys, sizeof(*keypad_pins),
> > + GFP_KERNEL);
> > + if (!keypad_pins)
> > + return -ENOMEM;
> > +
> > + ret = device_property_read_u32_array(dev, "adi,keypad-pins",
> > + keypad_pins, n_keys);
> > + if (ret)
> > + return ret;
> > +
> > + for (key = 0; key < n_keys; key++) {
> > + if (keypad_pins[key] >= n_pins)
> > + return -EINVAL;
> > + if (adp5585->has_pwm && keypad_pins[key] == ADP5585_ROW3)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid PWM pin being used in
> > the keypad\n");
> > + if (!info->has_row5 && keypad_pins[key] == ADP5585_ROW5)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid row5 being used in
> > the keypad\n");
> > + __set_bit(keypad_pins[key], adp5585->keypad);
> > + }
> > +
> > +no_keypad:
> > + ret = device_property_read_u32(dev, "adi,key-poll-ms", &prop_val);
> > + if (!ret) {
> > + switch (prop_val) {
> > + case 10:
> > + fallthrough;
> > + case 20:
> > + fallthrough;
> > + case 30:
> > + fallthrough;
> > + case 40:
> > + adp5585->key_poll_time = prop_val / 10 - 1;
> > + break;
> > + default:
> > + return dev_err_probe(dev, -EINVAL,
> > + "Invalid value(%u) for
> > adi,key-poll-ms\n",
> > + prop_val);
> > + }
> > + }
> > +
> > + ret = adp5585_keys_unlock_parse(adp5585);
> > + if (ret)
> > + return ret;
> > +
> > + return adp5585_keys_reset_parse(adp5585);
> > +}
> > +
> > +static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
> > +{
> > + unsigned int i;
> > +
> > + guard(mutex)(&adp5585->ev_lock);
> > +
> > + 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);
> > +
> > + if (key_val >= adp5585->info->gpi_ev_base &&
> > + key_val <= adp5585->info->gpi_ev_end) {
> > + unsigned int gpi = key_val - adp5585->info-
> > >gpi_ev_base;
> > +
> > + if (adp5585->gpio_irq_handle)
> > + adp5585->gpio_irq_handle(adp5585->gpio_dev,
> > gpi,
> > + key_press);
> > + } else if (adp5585->keys_irq_handle) {
> > + adp5585->keys_irq_handle(adp5585->input_dev,
> > key_val,
> > + key_press);
> > + }
> > + }
> > +}
> > +
> > +static irqreturn_t adp5585_irq(int irq, void *data)
> > +{
> > + struct adp5585_dev *adp5585 = data;
> > + unsigned int status, ev_cnt;
> > + int ret;
> > +
> > + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
> > + if (ret)
> > + return IRQ_HANDLED;
> > +
> > + if (status & ADP5585_OVRFLOW_INT)
> > + dev_err_ratelimited(adp5585->dev, "Event Overflow
> > Error\n");
> > +
> > + if (!(status & ADP5585_EVENT_INT))
> > + goto out_irq;
> > +
> > + ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
> > + if (ret)
> > + goto out_irq;
> > +
> > + ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
> > + if (!ev_cnt)
> > + goto out_irq;
> > +
> > + adp5585_report_events(adp5585, ev_cnt);
> > +out_irq:
> > + regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
> > + return IRQ_HANDLED;
> > +}
> > +
> > static void adp5585_osc_disable(void *data)
> > {
> > const struct adp5585_dev *adp5585 = data;
> > @@ -303,6 +706,128 @@ static void adp5585_osc_disable(void *data)
> > regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> > }
> >
> > +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->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,
> > + ®_val);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> > + adp5585->key_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)
> > {
> > const struct adp5585_info *info;
> > @@ -321,6 +846,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return -ENODEV;
> >
> > adp5585->info = info;
> > + adp5585->dev = &i2c->dev;
> > + adp5585->irq = i2c->irq;
> >
> > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > if (IS_ERR(adp5585->regmap))
> > @@ -337,12 +864,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > return dev_err_probe(&i2c->dev, -ENODEV,
> > "Invalid device ID 0x%02x\n", id);
> >
> > - ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > - ADP5585_OSC_EN);
> > + ret = adp5585_parse_fw(&i2c->dev, adp5585);
> > if (ret)
> > return ret;
> >
> > - ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable,
> > adp5585);
> > + ret = adp5585_setup(adp5585);
> > + if (ret)
> > + return ret;
> > +
> > + ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
> > if (ret)
> > return ret;
> >
> > @@ -353,13 +883,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;
> > @@ -368,11 +901,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
> > dffe1449de01dacf8fe78cf0e87d1f176d11f620..3ec542bed9c1c44899cc869d957557813e
> > 2d0aab 100644
> > --- a/include/linux/mfd/adp5585.h
> > +++ b/include/linux/mfd/adp5585.h
> > @@ -10,13 +10,21 @@
> > #define __MFD_ADP5585_H_
> >
> > #include <linux/bits.h>
> > +#include <linux/cleanup.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 +40,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
> > @@ -60,6 +69,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
> > @@ -104,8 +114,17 @@
> > #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
> >
> > +#define ADP5585_ROW3 3
> > +#define ADP5585_ROW4 4
> > +#define ADP5585_ROW5 5
> > +#define ADP5585_COL4 4
> > +#define ADP5585_MAX_UNLOCK_TIME_SEC 7
> > +#define ADP5585_GPI_EVENT_START 37
> > +#define ADP5585_GPI_EVENT_END 47
> > #define ADP5585_MAX_ROW_NUM 6
> > #define ADP5585_MAX_COL_NUM 5
> >
> > @@ -124,18 +143,38 @@
> > #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
> > #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
> > +#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
> > #define ADP5589_MAX_REG ADP5589_INT_EN
> >
> > +#define ADP5589_GPI_EVENT_START 97
> > +#define ADP5589_GPI_EVENT_END 115
> > #define ADP5589_MAX_ROW_NUM 8
> > #define ADP5589_MAX_COL_NUM 11
> >
> > @@ -154,11 +193,19 @@ struct adp5585_regs {
> > unsigned int gpo_out_a;
> > unsigned int gpio_dir_a;
> > unsigned int gpi_stat_a;
> > + unsigned int gpi_ev_a;
> > + unsigned int gpi_int_lvl_a;
> > unsigned int pwm_cfg;
> > unsigned int pwm_offt_low;
> > unsigned int pwm_ont_low;
> > + unsigned int reset_cfg;
> > unsigned int gen_cfg;
> > unsigned int ext_cfg;
> > + unsigned int pin_cfg_a;
> > + unsigned int poll_ptime_cfg;
> > + unsigned int int_en;
> > + unsigned int reset1_event_a;
> > + unsigned int reset2_event_a;
> > };
> >
> > struct adp5585_info {
> > @@ -169,6 +216,10 @@ struct adp5585_info {
> > unsigned int id;
> > u8 max_rows;
> > u8 max_cols;
> > + u8 gpi_ev_base;
> > + u8 gpi_ev_end;
> > + bool has_row5;
> > + bool has_unlock;
> > };
> >
> > struct regmap;
> > @@ -176,6 +227,51 @@ struct regmap;
> > struct adp5585_dev {
> > struct regmap *regmap;
> > const struct adp5585_info *info;
> > + struct device *dev;
> > + unsigned long *keypad;
> > + void (*gpio_irq_handle)(struct device *dev, unsigned int off,
> > + bool key_press);
> > + struct device *gpio_dev;
> > + void (*keys_irq_handle)(struct device *dev, unsigned int off,
> > + bool key_press);
> > + struct device *input_dev;
> > + /*
> > + * Used to synchronize usage (and availability) of
> > gpio_irq_handle()
> > + * and keys_irq_handle().
> > + */
> > + struct mutex ev_lock;
> > + int irq;
> > + u32 key_poll_time;
> > + u32 unlock_time;
> > + u32 unlock_keys[2];
> > + u32 nkeys_unlock;
> > + u32 reset1_keys[3];
> > + u32 nkeys_reset1;
> > + u32 reset2_keys[2];
> > + u32 nkeys_reset2;
> > + u8 reset_cfg;
> > + bool has_pwm;
> > };
> >
> > +static inline void adp5585_gpio_ev_handle_set(struct adp5585_dev *adp5585,
> > + void (*handle)(struct device
> > *dev,
> > + unsigned int
> > off,
> > + bool
> > key_press),
> > + struct device *gpio_dev)
> > +{
> > + guard(mutex)(&adp5585->ev_lock);
> > + adp5585->gpio_irq_handle = handle;
> > + adp5585->gpio_dev = gpio_dev;
> > +}
> > +
> > +static inline void adp5585_keys_ev_handle_set(struct adp5585_dev *adp5585,
> > + void (*handle)(struct device
> > *dev,
> > + unsigned int
> > off,
> > + bool
> > key_press),
> > + struct device *input_dev)
> > +{
> > + guard(mutex)(&adp5585->ev_lock);
> > + adp5585->keys_irq_handle = handle;
> > + adp5585->input_dev = input_dev;
> > +}
> > #endif
> >
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
2025-04-21 9:35 ` Laurent Pinchart
@ 2025-04-21 12:33 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:33 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 Mon, 2025-04-21 at 12:35 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:28PM +0100, Nuno Sá via B4 Relay wrote:
> > 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 | 221
> > ++++++++++++++++++++++++++++++++++
> > 4 files changed, 234 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index
> > b5acf50fc6af4322dec0dad2169b46c6a1903e3c..48bd39a1a96d9c57145cf2560eec542484
> > 27fc89 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -549,6 +549,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/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..322da0957067db77c7f66ab26a181d39c2
> > c1d513 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..f00ec003a59aa28577ae164c0539cc5aff
> > 9579fc 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..93961a9e822f8b10b1bca526b9486eed4a
> > d7f8f7
> > --- /dev/null
> > +++ b/drivers/input/keyboard/adp5585-keys.c
> > @@ -0,0 +1,221 @@
> > +// 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 {
> > + struct input_dev *input;
> > + unsigned short keycode[ADP5589_MAX_KEYMAPSIZE];
> > + struct device *dev;
> > + int row_shift;
> > + u8 max_rows;
> > + u8 max_cols;
> > +};
> > +
> > +static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
> > + struct adp5585_kpad *kpad)
> > +{
> > + unsigned long row_map, col_map;
> > + struct device *dev = kpad->dev;
> > + u32 cols = 0, rows = 0;
> > + int ret;
> > +
> > + row_map = bitmap_read(adp5585->keypad, 0, kpad->max_rows);
> > + col_map = bitmap_read(adp5585->keypad, kpad->max_rows, kpad-
> > >max_cols);
> > + /*
> > + * 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.
> > + */
> > + if (!bitmap_empty(&row_map, kpad->max_rows))
> > + rows = find_last_bit(&row_map, kpad->max_rows) + 1;
> > + if (!bitmap_empty(&col_map, kpad->max_cols))
> > + cols = find_last_bit(&col_map, kpad->max_cols) + 1;
> > +
> > + if (!rows && !cols)
> > + return dev_err_probe(dev, -EINVAL,
> > + "No rows or columns defined for the
> > keypad\n");
> > +
> > + if (cols && !rows)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Cannot have columns with no
> > rows!\n");
> > +
> > + if (rows && !cols)
> > + return dev_err_probe(dev, -EINVAL,
> > + "Cannot have rows with no
> > columns!\n");
> > +
> > + ret = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
> > + kpad->keycode, kpad->input);
> > + if (ret)
> > + return ret;
> > +
> > + kpad->row_shift = get_count_order(cols);
> > +
> > + if (device_property_present(kpad->dev, "autorepeat"))
> > + __set_bit(EV_REP, kpad->input->evbit);
> > +
> > + return 0;
> > +}
> > +
> > +static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
> > + struct adp5585_kpad *kpad)
> > +{
> > + unsigned long keys_bits, start = 0, nbits = kpad->max_rows;
> > + const struct adp5585_regs *regs = adp5585->info->regs;
> > + unsigned int i = 0, max_cols = kpad->max_cols;
> > + int ret;
> > +
> > + /*
> > + * 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(adp5585->keypad, start, nbits);
> > + if (keys_bits) {
> > + ret = regmap_write(adp5585->regmap, regs->pin_cfg_a
> > + i,
> > + keys_bits);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + start += nbits;
> > + if (max_cols > 8) {
> > + nbits = 8;
> > + max_cols -= nbits;
> > + } else {
> > + nbits = max_cols;
> > + }
> > +
> > + i++;
> > + } while (start < kpad->max_rows + kpad->max_cols);
> > +
> > + return 0;
> > +}
> > +
> > +static void adp5585_keys_ev_handle(struct device *dev, unsigned int key,
> > + bool key_press)
> > +{
> > + struct adp5585_kpad *kpad = dev_get_drvdata(dev);
> > + unsigned int row, col, code;
> > +
> > + row = (key - 1) / (kpad->max_cols);
> > + col = (key - 1) % (kpad->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);
> > +}
> > +
> > +static void adp5585_keys_ev_handle_clean(void *adp5585)
> > +{
> > + adp5585_keys_ev_handle_set(adp5585, NULL, NULL);
> > +}
> > +
> > +static int adp5585_keys_probe(struct platform_device *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 ret;
> > +
> > + 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");
>
> This causes the following messages to be printed in the kernel log on a
> platform where the keypad feature is not declared in DT:
>
> [ 11.625591] adp5585-keys adp5585-keys.1.auto: error -EINVAL: IRQ is
> mandatory for the keypad
> [ 11.625637] adp5585-keys adp5585-keys.1.auto: probe with driver adp5585-
> keys failed with error -22
>
> The MFD driver should detect which features are declared, and only
> register the corresponding MFD cells.
Oh right, the above does not look right!
- Nuno Sá
>
> > +
> > + kpad->dev = dev;
> > + kpad->max_cols = adp5585->info->max_cols;
> > + kpad->max_rows = adp5585->info->max_rows;
> > +
> > + kpad->input = devm_input_allocate_device(dev);
> > + if (!kpad->input)
> > + return -ENOMEM;
> > +
> > + ret = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "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->dev.parent = dev;
> > +
> > + input_set_drvdata(kpad->input, kpad);
> > +
> > + 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);
> > +
> > + ret = adp5585_keys_parse_fw(adp5585, kpad);
> > + if (ret)
> > + return ret;
> > +
> > + ret = adp5585_keys_setup(adp5585, kpad);
> > + if (ret)
> > + return ret;
> > +
> > + platform_set_drvdata(pdev, kpad);
> > + adp5585_keys_ev_handle_set(adp5585, adp5585_keys_ev_handle, dev);
> > + ret = devm_add_action_or_reset(dev, adp5585_keys_ev_handle_clean,
> > + adp5585);
> > + if (ret)
> > + return ret;
> > +
> > + return input_register_device(kpad->input);
> > +}
> > +
> > +static const struct platform_device_id adp5585_keys_id_table[] = {
> > + { "adp5585-keys" },
> > + { "adp5589-keys" },
> > + { }
> > +};
> > +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");
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 13/17] Input: adp5589: remove the driver
2025-04-21 9:40 ` Laurent Pinchart
@ 2025-04-21 12:34 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:34 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 Mon, 2025-04-21 at 12:40 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:29PM +0100, Nuno Sá via B4 Relay wrote:
> > 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")
>
> Those commit IDs won't exist in mainline. With that fixed,
Hmm, I'll fix this...
>
> > is doing.
> >
> > 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 ------------------------------
> > ---
>
> Shouldn't you also drop include/linux/input/adp5589.h ?
>
Definitely...
> With those issues fixed,
>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> > 3 files changed, 1077 deletions(-)
> >
> > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> > index
> > 322da0957067db77c7f66ab26a181d39c2c1d513..76d3397961fa006de4d5979e134b8c6e7d
> > d52c73 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..8bc20ab2b103b0b75c446e4aa919dad01a
> > a5f405 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..0000000000000000000000000000000000
> > 000000
> > --- 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");
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator
2025-04-21 9:48 ` Laurent Pinchart
@ 2025-04-21 12:38 ` Nuno Sá
2025-04-21 22:09 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12: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 Mon, 2025-04-21 at 12:48 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:30PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > Make sure we get and enable the VDD supply (if available).
>
> Can the regulator be enabled only when needed ?
>
Hmm, I guess we could do that for the case where only the PWM device is
"enabled". That said, I don't think the extra complexity for that really pays
off...
- Nuno Sá
> > 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
> > c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b..667cc5bd0745f64eec60837ec3c00057af
> > 0cddeb 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -18,6 +18,7 @@
> > #include <linux/mod_devicetable.h>
> > #include <linux/module.h>
> > #include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > #include <linux/types.h>
> >
> > static const struct mfd_cell adp5585_devs[] = {
> > @@ -849,6 +850,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > adp5585->dev = &i2c->dev;
> > adp5585->irq = i2c->irq;
> >
> > + ret = devm_regulator_get_enable(&i2c->dev, "vdd");
> > + if (ret)
> > + return ret;
> > +
> > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > if (IS_ERR(adp5585->regmap))
> > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 16/17] mfd: adp5585: add support for a reset pin
2025-04-21 9:46 ` Laurent Pinchart
@ 2025-04-21 12:42 ` Nuno Sá
2025-04-21 22:10 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:42 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 Mon, 2025-04-21 at 12:46 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:32PM +0100, 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 | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
> >
> > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > index
> > 667cc5bd0745f64eec60837ec3c00057af0cddeb..2af110fb28054c8ad6709a9a42cb21919b
> > e433e7 100644
> > --- a/drivers/mfd/adp5585.c
> > +++ b/drivers/mfd/adp5585.c
> > @@ -12,6 +12,7 @@
> > #include <linux/device.h>
> > #include <linux/err.h>
> > #include <linux/i2c.h>
> > +#include <linux/gpio/consumer.h>
>
> Alphabetic order please.
>
> > #include <linux/mfd/adp5585.h>
> > #include <linux/mfd/core.h>
> > #include <linux/minmax.h>
> > @@ -833,6 +834,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > {
> > const struct adp5585_info *info;
> > struct adp5585_dev *adp5585;
> > + struct gpio_desc *gpio;
> > unsigned int id;
> > int ret;
> >
> > @@ -854,6 +856,16 @@ 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);
> > +
> > + if (gpio) {
> > + fsleep(30);
> > + gpiod_set_value_cansleep(gpio, 0);
> > + fsleep(60);
>
> Where do those values come from ? The only possibly related timing
> information I found in the datasheet indicate a GPIO debounce delay of
> 70µs.
Yeps this were just some reasonable values that work. The datasheet does not
mention any timing...
I can add a comment if you like stating there's no defined values in the
datasheet and these values are "just" some values that work.
- Nuno Sá
>
> > + }
> > +
> > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > if (IS_ERR(adp5585->regmap))
> > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-21 12:29 ` Laurent Pinchart
@ 2025-04-21 12:45 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-21 12:45 UTC (permalink / raw)
To: Laurent Pinchart
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,
Dmitry Torokhov, Liu Ying
On Mon, 2025-04-21 at 15:29 +0300, Laurent Pinchart wrote:
> On Mon, Apr 21, 2025 at 01:12:33PM +0100, Nuno Sá wrote:
> > On Mon, 2025-04-21 at 11:56 +0300, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:17PM +0100, Nuno Sá via B4 Relay wrote:
> > > > 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.
> > >
> > > This seems to make sense.
> > >
> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > >
> > > I however expected changes in this series to *not* register MFD cells
> > > for the devices not enabled in DT. Could you do so in v3, on top of this
> > > patch ?
> >
> > Makes sense... In theory, I would go with MFD_CELL_OF() but that would need
> > (I
> > guess) bindings for all the devices and since PWM and GPIO were not
> > introduced
> > with that...
>
> I initially designed the bindings with child nodes, but got told to
> instead use a single DT node :-(
>
Interesting, I would expect the child nodes approach to be encouraged...
> > Anyways, I'll look into some "mandatory" property for each of the supported
> > cells and use that as deciding point.
>
> Sounds good to me.
>
> > > > 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..e30e22f964f78519b2ec207e9415e4
> > > > 897d
> > > > b5c702 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:
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
2025-04-21 8:56 ` Laurent Pinchart
@ 2025-04-21 18:57 ` Rob Herring (Arm)
1 sibling, 0 replies; 81+ messages in thread
From: Rob Herring (Arm) @ 2025-04-21 18:57 UTC (permalink / raw)
To: Nuno Sá
Cc: linux-gpio, devicetree, Krzysztof Kozlowski,
Uwe Kleine-König, Linus Walleij, Lee Jones, Liu Ying,
Laurent Pinchart, Bartosz Golaszewski, linux-input, Conor Dooley,
linux-pwm, Dmitry Torokhov
On Tue, 15 Apr 2025 15:49:17 +0100, Nuno Sá wrote:
> 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.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> Documentation/devicetree/bindings/mfd/adi,adp5585.yaml | 3 ---
> 1 file changed, 3 deletions(-)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
2025-04-15 14:49 ` [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
2025-04-21 9:07 ` Laurent Pinchart
@ 2025-04-21 18:59 ` Rob Herring (Arm)
1 sibling, 0 replies; 81+ messages in thread
From: Rob Herring (Arm) @ 2025-04-21 18:59 UTC (permalink / raw)
To: Nuno Sá
Cc: Lee Jones, Linus Walleij, linux-input, Uwe Kleine-König,
Liu Ying, Conor Dooley, linux-pwm, Bartosz Golaszewski,
Krzysztof Kozlowski, linux-gpio, Laurent Pinchart, devicetree,
Dmitry Torokhov
On Tue, 15 Apr 2025 15:49:21 +0100, Nuno Sá wrote:
> 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.
>
> 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(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe
2025-04-21 12:14 ` Nuno Sá
@ 2025-04-21 22:03 ` Laurent Pinchart
2025-04-22 7:50 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 22:03 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,
Dmitry Torokhov, Liu Ying
Hi Nuno,
On Mon, Apr 21, 2025 at 01:14:28PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 11:57 +0300, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:18PM +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).
> >
> > Setting this bit unconditionally increases power consumption. It should
> > only be set when needed.
>
> Well, not sure if the effort for that pays off... The only usecase were it would
> make sense to do that would be for PWM. For the other devices (and assuming I'm
> right with the GPI case) we need this always set.
For the keypad, can't the device be kept powered off if the input device
exposed to userspace is not open ? And for GPIOs, OSC_EN isn't needed
when all requested GPIOs are configured as outputs, as far as I can
tell.
I'm fine addressing this issue on top of this series.
> > > 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
> > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..f17b5f2474cac6a403556694066f438288
> > > 264a49 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -110,6 +110,13 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> > > },
> > > };
> > >
> > > +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;
> > > @@ -138,6 +145,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > return dev_err_probe(&i2c->dev, -ENODEV,
> > > "Invalid device ID 0x%02x\n", id);
> > >
> > > + 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,
> > > adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > > NULL, 0, NULL);
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-21 12:21 ` Nuno Sá
@ 2025-04-21 22:06 ` Laurent Pinchart
2025-04-22 7:51 ` Nuno Sá
2025-04-24 16:01 ` Lee Jones
0 siblings, 2 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 22:06 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,
Dmitry Torokhov, Liu Ying
Hi Nuno,
On Mon, Apr 21, 2025 at 01:21:08PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 12:15 +0300, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:22PM +0100, Nuno Sá via B4 Relay wrote:
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > programmable logic, reset generator, and PWM generator.
> > >
> > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > drivers. Most importantly, we need to differentiate between some
> > > registers addresses. It also hints to future keymap support.
> >
> > Please split this in two patches, one that reworks the driver to support
> > different register addresses, and one that adds adp5589 support.
> >
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d94667993410610be7fc3672 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
> > >
> > > };
> > >
> > > +static const struct mfd_cell adp5589_devs[] = {
> > > + MFD_CELL_NAME("adp5589-keys"),
> > > + 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),
> > > };
> > > @@ -34,6 +41,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:
> > > @@ -77,10 +93,52 @@ 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,
> > > +};
> > > +
> > > enum adp5585_regmap_type {
> > > ADP5585_REGMAP_00,
> > > ADP5585_REGMAP_02,
> > > ADP5585_REGMAP_04,
> > > + ADP5589_REGMAP_00,
> > > + ADP5589_REGMAP_01,
> > > + ADP5589_REGMAP_02,
> > > };
> > >
> > > static const struct regmap_config adp5585_regmap_configs[] = {
> > > @@ -111,6 +169,131 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> > > .reg_defaults_raw = adp5585_regmap_defaults_04,
> > > .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> > > },
> > > + [ADP5589_REGMAP_00] = {
> > > + .reg_bits = 8,
> > > + .val_bits = 8,
> > > + .max_register = ADP5589_MAX_REG,
> > > + .volatile_table = &adp5589_volatile_regs,
> > > + .cache_type = REGCACHE_MAPLE,
> > > + .reg_defaults_raw = adp5589_regmap_defaults_00,
> > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_00),
> > > + },
> > > + [ADP5589_REGMAP_01] = {
> > > + .reg_bits = 8,
> > > + .val_bits = 8,
> > > + .max_register = ADP5589_MAX_REG,
> > > + .volatile_table = &adp5589_volatile_regs,
> > > + .cache_type = REGCACHE_MAPLE,
> > > + .reg_defaults_raw = adp5589_regmap_defaults_01,
> > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_01),
> > > + },
> > > + [ADP5589_REGMAP_02] = {
> > > + .reg_bits = 8,
> > > + .val_bits = 8,
> > > + .max_register = ADP5589_MAX_REG,
> > > + .volatile_table = &adp5589_volatile_regs,
> > > + .cache_type = REGCACHE_MAPLE,
> > > + .reg_defaults_raw = adp5589_regmap_defaults_02,
> > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_02),
> > > + },
> > > +};
> > > +
> > > +static const struct adp5585_regs adp5585_regs = {
> > > + .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,
> > > + .pwm_cfg = ADP5585_PWM_CFG,
> > > + .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > > + .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > > + .gen_cfg = ADP5585_GENERAL_CFG,
> > > + .ext_cfg = ADP5585_PIN_CONFIG_C,
> > > +};
> >
> > Why does this need to be stored in this driver, and not in the drivers
> > for the gpio and pwm cells ? If the kernel is compiled without e.g. the
> > adp5585-pwm driver, we shouldn't waste memory here by adding data that
> > only the adp5585-pwm driver needs.
>
> I don't really think the memory we would save to be that relevant but I can
> better separate things. I guess i went like this because there's some shared
> variables that will have to be in the top level structs and I did not wanted to
> have a "global" and "local" regs thingy...
I understand, and I think it's at least partly a coding style
preference. Personally, I find that having child-specific data in child
drivers makes the code easier to read, as it increases locality.
Otherwise, I have to look through multiple child drivers to see if and
where each field is used.
> > > +
> > > +static const struct adp5585_regs adp5589_regs = {
> > > + .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,
> > > + .pwm_cfg = ADP5589_PWM_CFG,
> > > + .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> > > + .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> > > + .gen_cfg = ADP5589_GENERAL_CFG,
> > > + .ext_cfg = ADP5589_PIN_CONFIG_D,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5585_info = {
> > > + .adp5585_devs = adp5585_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > + .id = ADP5585_MAN_ID_VALUE,
> > > + .regs = &adp5585_regs,
> > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > + .max_cols = ADP5585_MAX_COL_NUM,
> >
> > Same here, the max_rows and max_cols fields don't seem to belong to this
> > driver.
> >
> > > +};
> > > +
> > > +static const struct adp5585_info adp5585_01_info = {
> > > + .adp5585_devs = adp5585_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > + .id = ADP5585_MAN_ID_VALUE,
> > > + .regs = &adp5585_regs,
> > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5585_02_info = {
> > > + .adp5585_devs = adp5585_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > + .id = ADP5585_MAN_ID_VALUE,
> > > + .regs = &adp5585_regs,
> > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5585_04_info = {
> > > + .adp5585_devs = adp5585_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > + .id = ADP5585_MAN_ID_VALUE,
> > > + .regs = &adp5585_regs,
> > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5589_info = {
> > > + .adp5585_devs = adp5589_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > + .id = ADP5589_MAN_ID_VALUE,
> > > + .regs = &adp5589_regs,
> > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5589_01_info = {
> > > + .adp5585_devs = adp5589_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > + .id = ADP5589_MAN_ID_VALUE,
> > > + .regs = &adp5589_regs,
> > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > +};
> > > +
> > > +static const struct adp5585_info adp5589_02_info = {
> > > + .adp5585_devs = adp5589_devs,
> > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > + .id = ADP5589_MAN_ID_VALUE,
> > > + .regs = &adp5589_regs,
> > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > };
> > >
> > > static void adp5585_osc_disable(void *data)
> > > @@ -122,7 +305,7 @@ static void adp5585_osc_disable(void *data)
> > >
> > > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > {
> > > - const struct regmap_config *regmap_config;
> > > + const struct adp5585_info *info;
> > > struct adp5585_dev *adp5585;
> > > unsigned int id;
> > > int ret;
> > > @@ -133,8 +316,13 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > >
> > > i2c_set_clientdata(i2c, adp5585);
> > >
> > > - regmap_config = i2c_get_match_data(i2c);
> > > - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> > > + info = i2c_get_match_data(i2c);
> > > + if (!info)
> > > + return -ENODEV;
> >
> > Can this happen ?
> >
> > > +
> > > + adp5585->info = info;
> >
> > Drop the local info variable and assign the value to adp5585->info
> > directly.
> >
> > > +
> > > + adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > > if (IS_ERR(adp5585->regmap))
> > > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
> > > "Failed to initialize register
> > > map\n");
> > > @@ -144,7 +332,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);
> > >
> > > @@ -158,8 +347,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > return ret;
> > >
> > > ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > > - adp5585_devs, ARRAY_SIZE(adp5585_devs),
> > > - NULL, 0, NULL);
> > > + adp5585->info->adp5585_devs,
> > > + adp5585->info->n_devs, NULL, 0, NULL);
> > > if (ret)
> > > return dev_err_probe(&i2c->dev, ret,
> > > "Failed to add child devices\n");
> > > @@ -191,19 +380,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm,
> > > adp5585_suspend, adp5585_resume);
> > > static const struct of_device_id adp5585_of_match[] = {
> > > {
> > > .compatible = "adi,adp5585-00",
> > > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > + .data = &adp5585_info,
> > > }, {
> > > .compatible = "adi,adp5585-01",
> > > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > + .data = &adp5585_01_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,
> > > + }, {
> > > + .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
> > > 016033cd68e46757aca86d21dd37025fd354b801..dffe1449de01dacf8fe78cf0e87d1f176d
> > > 11f620 100644
> > > --- a/include/linux/mfd/adp5585.h
> > > +++ b/include/linux/mfd/adp5585.h
> > > @@ -104,9 +104,11 @@
> > > #define ADP5585_INT_CFG BIT(1)
> > > #define ADP5585_RST_CFG BIT(0)
> > > #define ADP5585_INT_EN 0x3c
> > > -
> > > #define ADP5585_MAX_REG ADP5585_INT_EN
> > >
> > > +#define ADP5585_MAX_ROW_NUM 6
> > > +#define ADP5585_MAX_COL_NUM 5
> > > +
> > > /*
> > > * 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
> > > @@ -117,10 +119,63 @@
> > > #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_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
> >
> > Indentation looks wrong.
> >
> > > +#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_GENERAL_CFG 0x4d
> > > +#define ADP5589_INT_EN 0x4e
> > > +#define ADP5589_MAX_REG ADP5589_INT_EN
> > > +
> > > +#define ADP5589_MAX_ROW_NUM 8
> > > +#define ADP5589_MAX_COL_NUM 11
> > > +
> > > +/*
> > > + * 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_regs {
> > > + 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 pwm_cfg;
> > > + unsigned int pwm_offt_low;
> > > + unsigned int pwm_ont_low;
> > > + unsigned int gen_cfg;
> > > + unsigned int ext_cfg;
> > > +};
> > > +
> > > +struct adp5585_info {
> > > + const struct mfd_cell *adp5585_devs;
> > > + const struct regmap_config *regmap_config;
> > > + const struct adp5585_regs *regs;
> > > + unsigned int n_devs;
> > > + unsigned int id;
> > > + u8 max_rows;
> > > + u8 max_cols;
> > > +};
> > > +
> > > struct regmap;
> > >
> > > struct adp5585_dev {
> > > struct regmap *regmap;
> > > + const struct adp5585_info *info;
> > > };
> > >
> > > #endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator
2025-04-21 12:38 ` Nuno Sá
@ 2025-04-21 22:09 ` Laurent Pinchart
2025-04-22 8:12 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 22:09 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,
Dmitry Torokhov, Liu Ying
Hi Nuno,
On Mon, Apr 21, 2025 at 01:38:47PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 12:48 +0300, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:30PM +0100, Nuno Sá via B4 Relay wrote:
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > Make sure we get and enable the VDD supply (if available).
> >
> > Can the regulator be enabled only when needed ?
> >
>
> Hmm, I guess we could do that for the case where only the PWM device is
> "enabled". That said, I don't think the extra complexity for that really pays
> off...
Or when no GPIO is requested, or when the input device for the keypad is
not open ? Then can be handled with power optimization for OSC_EN on top
of this series.
> > > 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
> > > c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b..667cc5bd0745f64eec60837ec3c00057af
> > > 0cddeb 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -18,6 +18,7 @@
> > > #include <linux/mod_devicetable.h>
> > > #include <linux/module.h>
> > > #include <linux/regmap.h>
> > > +#include <linux/regulator/consumer.h>
> > > #include <linux/types.h>
> > >
> > > static const struct mfd_cell adp5585_devs[] = {
> > > @@ -849,6 +850,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > adp5585->dev = &i2c->dev;
> > > adp5585->irq = i2c->irq;
> > >
> > > + ret = devm_regulator_get_enable(&i2c->dev, "vdd");
> > > + if (ret)
> > > + return ret;
> > > +
> > > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > > if (IS_ERR(adp5585->regmap))
> > > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 16/17] mfd: adp5585: add support for a reset pin
2025-04-21 12:42 ` Nuno Sá
@ 2025-04-21 22:10 ` Laurent Pinchart
0 siblings, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 22:10 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,
Dmitry Torokhov, Liu Ying
On Mon, Apr 21, 2025 at 01:42:43PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 12:46 +0300, Laurent Pinchart wrote:
> > Hi Nuno,
> >
> > Thank you for the patch.
> >
> > On Tue, Apr 15, 2025 at 03:49:32PM +0100, 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 | 12 ++++++++++++
> > > 1 file changed, 12 insertions(+)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > 667cc5bd0745f64eec60837ec3c00057af0cddeb..2af110fb28054c8ad6709a9a42cb21919b
> > > e433e7 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -12,6 +12,7 @@
> > > #include <linux/device.h>
> > > #include <linux/err.h>
> > > #include <linux/i2c.h>
> > > +#include <linux/gpio/consumer.h>
> >
> > Alphabetic order please.
> >
> > > #include <linux/mfd/adp5585.h>
> > > #include <linux/mfd/core.h>
> > > #include <linux/minmax.h>
> > > @@ -833,6 +834,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > {
> > > const struct adp5585_info *info;
> > > struct adp5585_dev *adp5585;
> > > + struct gpio_desc *gpio;
> > > unsigned int id;
> > > int ret;
> > >
> > > @@ -854,6 +856,16 @@ 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);
> > > +
> > > + if (gpio) {
> > > + fsleep(30);
> > > + gpiod_set_value_cansleep(gpio, 0);
> > > + fsleep(60);
> >
> > Where do those values come from ? The only possibly related timing
> > information I found in the datasheet indicate a GPIO debounce delay of
> > 70µs.
>
> Yeps this were just some reasonable values that work. The datasheet does not
> mention any timing...
>
> I can add a comment if you like stating there's no defined values in the
> datasheet and these values are "just" some values that work.
If you could explain that in the commit message, it would be nice.
> > > + }
> > > +
> > > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > > if (IS_ERR(adp5585->regmap))
> > > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-21 12:32 ` Nuno Sá
@ 2025-04-21 22:13 ` Laurent Pinchart
0 siblings, 0 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-21 22:13 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,
Dmitry Torokhov, Liu Ying
Hi Nuno,
On Mon, Apr 21, 2025 at 01:32:41PM +0100, Nuno Sá wrote:
> On Mon, 2025-04-21 at 12:33 +0300, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > This adds support for key events. Basically, it adds support for all the
> > > FW parsing and configuration of the keys (also making sure there's no
> > > overlaps). They can either be part of a matrix keymap (in which case
> > > events will be handled by an input device) or they can be gpi's (in which
> > > case events will be handled by the gpiochip device via gpio_keys). There's
> > > also support for unlock keys as for reset keys.
> >
> > There's lots of code below specific to matrix keypad handling. Please
> > move it to the keypad driver, and keep this driver as lean as possible.
>
> Yeah, I gave this a lot of thought and initially I was doing it in the input
> driver (even giving that device it's own of_compatible) but then I went this
> way. Basically because unlock and reset keys can also be done through gpio-keys
> which is supported in the GPIO driver. Therefore, since these features are
> shared between GPIO and INPUT, it made sense to me to put all of this in the top
> device. IOW, we can still have these features even if we're not using the input
> device.
>
> > > This patch adds all the foundation needed by subsequent changes where the
> > > child devices (actually handling the events) can "register" a callback
> > > to handle the event.
> > >
> > > Also to note that enabling the internal oscillator in now done as part of
> > > adp5585_setup().
> > >
> > > We also enable/disable the IRQ (if present) in the corresponding PM
> > > event.
> >
> > The patch does too many things. The base rule should be "one logical
> > change, one patch". At the very least the event reporting mechanism
> > and the keypad handling should be split in two different patches.
>
> Both things are related... Instead maybe we could split things as:
>
> 1) Add key handling (which also means DT parsing + device setup + IRQs)
I'm still wondering if we could split the event reporting mechanism and
the keypad handling. The latter depends on the former, but events can be
used for more purposes than keypad handling.
> 2) Reset keys support
> 3) Unlock keys support
This seems good.
> Thoughts?
>
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 551 +++++++++++++++++++++++++++++++++++++++++++-
> > > include/linux/mfd/adp5585.h | 96 ++++++++
> > > 2 files changed, 642 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > index
> > > c3586c0d6aa2e7e7d94667993410610be7fc3672..c1d51d50dca6c9367d4a1b98a4f8bbec12
> > > dbf90b 100644
> > > --- a/drivers/mfd/adp5585.c
> > > +++ b/drivers/mfd/adp5585.c
> > > @@ -8,11 +8,13 @@
> > > */
> > >
> > > #include <linux/array_size.h>
> > > +#include <linux/bitfield.h>
> > > #include <linux/device.h>
> > > #include <linux/err.h>
> > > #include <linux/i2c.h>
> > > #include <linux/mfd/adp5585.h>
> > > #include <linux/mfd/core.h>
> > > +#include <linux/minmax.h>
> > > #include <linux/mod_devicetable.h>
> > > #include <linux/module.h>
> > > #include <linux/regmap.h>
> > > @@ -205,11 +207,19 @@ static const struct adp5585_regs adp5585_regs = {
> > > .gpo_out_a = ADP5585_GPO_OUT_MODE_A,
> > > .gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
> > > .gpi_stat_a = ADP5585_GPI_STATUS_A,
> > > + .gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
> > > + .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
> > > .pwm_cfg = ADP5585_PWM_CFG,
> > > .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > > .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > > + .reset_cfg = ADP5585_RESET_CFG,
> > > .gen_cfg = ADP5585_GENERAL_CFG,
> > > .ext_cfg = ADP5585_PIN_CONFIG_C,
> > > + .pin_cfg_a = ADP5585_PIN_CONFIG_A,
> > > + .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
> > > + .int_en = ADP5585_INT_EN,
> > > + .reset1_event_a = ADP5585_RESET1_EVENT_A,
> > > + .reset2_event_a = ADP5585_RESET2_EVENT_A,
> > > };
> > >
> > > static const struct adp5585_regs adp5589_regs = {
> > > @@ -219,11 +229,19 @@ static const struct adp5585_regs adp5589_regs = {
> > > .gpo_out_a = ADP5589_GPO_OUT_MODE_A,
> > > .gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
> > > .gpi_stat_a = ADP5589_GPI_STATUS_A,
> > > + .gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
> > > + .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
> > > .pwm_cfg = ADP5589_PWM_CFG,
> > > .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> > > .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> > > + .reset_cfg = ADP5589_RESET_CFG,
> > > .gen_cfg = ADP5589_GENERAL_CFG,
> > > .ext_cfg = ADP5589_PIN_CONFIG_D,
> > > + .pin_cfg_a = ADP5589_PIN_CONFIG_A,
> > > + .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
> > > + .int_en = ADP5589_INT_EN,
> > > + .reset1_event_a = ADP5589_RESET1_EVENT_A,
> > > + .reset2_event_a = ADP5589_RESET2_EVENT_A,
> > > };
> > >
> > > static const struct adp5585_info adp5585_info = {
> > > @@ -234,6 +252,8 @@ static const struct adp5585_info adp5585_info = {
> > > .regs = &adp5585_regs,
> > > .max_rows = ADP5585_MAX_ROW_NUM,
> > > .max_cols = ADP5585_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5585_01_info = {
> > > @@ -241,9 +261,12 @@ static const struct adp5585_info adp5585_01_info = {
> > > .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > .n_devs = ARRAY_SIZE(adp5585_devs),
> > > .id = ADP5585_MAN_ID_VALUE,
> > > + .has_row5 = true,
> > > .regs = &adp5585_regs,
> > > .max_rows = ADP5585_MAX_ROW_NUM,
> > > .max_cols = ADP5585_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5585_02_info = {
> > > @@ -254,6 +277,8 @@ static const struct adp5585_info adp5585_02_info = {
> > > .regs = &adp5585_regs,
> > > .max_rows = ADP5585_MAX_ROW_NUM,
> > > .max_cols = ADP5585_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5585_04_info = {
> > > @@ -264,6 +289,8 @@ static const struct adp5585_info adp5585_04_info = {
> > > .regs = &adp5585_regs,
> > > .max_rows = ADP5585_MAX_ROW_NUM,
> > > .max_cols = ADP5585_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5585_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5585_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5589_info = {
> > > @@ -271,9 +298,13 @@ static const struct adp5585_info adp5589_info = {
> > > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> > > .n_devs = ARRAY_SIZE(adp5589_devs),
> > > .id = ADP5589_MAN_ID_VALUE,
> > > + .has_row5 = true,
> > > + .has_unlock = true,
> > > .regs = &adp5589_regs,
> > > .max_rows = ADP5589_MAX_ROW_NUM,
> > > .max_cols = ADP5589_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5589_01_info = {
> > > @@ -281,9 +312,13 @@ static const struct adp5585_info adp5589_01_info = {
> > > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> > > .n_devs = ARRAY_SIZE(adp5589_devs),
> > > .id = ADP5589_MAN_ID_VALUE,
> > > + .has_row5 = true,
> > > + .has_unlock = true,
> > > .regs = &adp5589_regs,
> > > .max_rows = ADP5589_MAX_ROW_NUM,
> > > .max_cols = ADP5589_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > > };
> > >
> > > static const struct adp5585_info adp5589_02_info = {
> > > @@ -291,11 +326,379 @@ static const struct adp5585_info adp5589_02_info = {
> > > .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> > > .n_devs = ARRAY_SIZE(adp5589_devs),
> > > .id = ADP5589_MAN_ID_VALUE,
> > > + .has_row5 = true,
> > > + .has_unlock = true,
> > > .regs = &adp5589_regs,
> > > .max_rows = ADP5589_MAX_ROW_NUM,
> > > .max_cols = ADP5589_MAX_COL_NUM,
> > > + .gpi_ev_base = ADP5589_GPI_EVENT_START,
> > > + .gpi_ev_end = ADP5589_GPI_EVENT_END,
> > > };
> > >
> > > +static int adp5585_keys_validate_key(const struct adp5585_dev *adp5585, u32
> > > key,
> > > + bool is_gpi)
> > > +{
> > > + const struct adp5585_info *info = adp5585->info;
> > > + struct device *dev = adp5585->dev;
> > > + u32 row, col;
> > > +
> > > + if (is_gpi) {
> > > + u32 gpi = key - adp5585->info->gpi_ev_base;
> > > +
> > > + if (!info->has_row5 && gpi == ADP5585_ROW5)
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid unlock/reset GPI(%u)
> > > not supported\n",
> > > + gpi);
> > > +
> > > + /* check if it's being used in the keypad */
> > > + if (test_bit(gpi, adp5585->keypad))
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid unlock/reset GPI(%u)
> > > being used in the keypad\n",
> > > + gpi);
> > > +
> > > + return 0;
> > > + }
> > > +
> > > + row = (key - 1) / info->max_cols;
> > > + col = (key - 1) % info->max_cols;
> > > +
> > > + /* both the row and col must be part of the keypad */
> > > + if (test_bit(row, adp5585->keypad) &&
> > > + test_bit(col + info->max_rows, adp5585->keypad))
> > > + return 0;
> > > +
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid unlock/reset key(%u) not used in the
> > > keypad\n", key);
> > > +}
> > > +
> > > +static int adp5585_keys_parse_array(const struct adp5585_dev *adp5585,
> > > + const char *prop, u32 *keys, u32
> > > *n_keys,
> > > + u32 max_keys, bool reset_key)
> > > +{
> > > + const struct adp5585_info *info = adp5585->info;
> > > + struct device *dev = adp5585->dev;
> > > + unsigned int key, max_keypad;
> > > + int ret;
> > > +
> > > + ret = device_property_count_u32(dev, prop);
> > > + if (ret < 0)
> > > + return 0;
> > > +
> > > + *n_keys = ret;
> > > +
> > > + if (!info->has_unlock && !reset_key)
> > > + return dev_err_probe(dev, -EOPNOTSUPP,
> > > + "Unlock keys not supported\n");
> > > +
> > > + if (*n_keys > max_keys)
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid number of keys(%u > %u) for
> > > %s\n",
> > > + *n_keys, max_keys, prop);
> > > +
> > > + ret = device_property_read_u32_array(dev, prop, keys, *n_keys);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + max_keypad = adp5585->info->max_rows * adp5585->info->max_cols;
> > > +
> > > + for (key = 0; key < *n_keys; key++) {
> > > + /* part of the keypad... */
> > > + if (in_range(keys[key], 1, max_keypad)) {
> > > + /* is it part of the keypad?! */
> > > + ret = adp5585_keys_validate_key(adp5585, keys[key],
> > > + false);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + continue;
> > > + }
> > > +
> > > + /* part of gpio-keys... */
> > > + if (in_range(keys[key], adp5585->info->gpi_ev_base,
> > > + info->max_cols + info->max_rows)) {
> > > + /* is the GPI being used as part of the keypad?! */
> > > + ret = adp5585_keys_validate_key(adp5585, keys[key],
> > > + true);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + continue;
> > > + }
> > > +
> > > + if (!reset_key && keys[key] == 127)
> > > + continue;
> > > +
> > > + return dev_err_probe(dev, -EINVAL, "Invalid key(%u) for
> > > %s\n",
> > > + keys[key], prop);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int adp5585_keys_unlock_parse(struct adp5585_dev *adp5585)
> > > +{
> > > + struct device *dev = adp5585->dev;
> > > + int ret;
> > > +
> > > + ret = adp5585_keys_parse_array(adp5585, "adi,unlock-keys",
> > > + adp5585->unlock_keys,
> > > + &adp5585->nkeys_unlock,
> > > + ARRAY_SIZE(adp5585->unlock_keys),
> > > false);
> > > + if (ret)
> > > + return ret;
> > > + if (!adp5585->nkeys_unlock)
> > > + /* no unlock keys */
> > > + 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_keys_reset_parse(struct adp5585_dev *adp5585)
> > > +{
> > > + const struct adp5585_info *info = adp5585->info;
> > > + struct device *dev = adp5585->dev;
> > > + u32 prop_val;
> > > + int ret;
> > > +
> > > + ret = adp5585_keys_parse_array(adp5585, "adi,reset1-keys",
> > > + adp5585->reset1_keys,
> > > + &adp5585->nkeys_reset1,
> > > + ARRAY_SIZE(adp5585->reset1_keys),
> > > true);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + if (adp5585->nkeys_reset1 > 0) {
> > > + if (test_bit(ADP5585_ROW4, adp5585->keypad))
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid reset1 output(R4)
> > > being used in the keypad\n");
> > > +
> > > + if (device_property_read_bool(dev, "adi,reset1-active-
> > > high"))
> > > + adp5585->reset_cfg |=
> > > FIELD_PREP(ADP5585_RESET1_POL, 1);
> > > + }
> > > +
> > > + ret = adp5585_keys_parse_array(adp5585, "adi,reset2-keys",
> > > + adp5585->reset2_keys,
> > > + &adp5585->nkeys_reset2,
> > > + ARRAY_SIZE(adp5585->reset2_keys),
> > > true);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + if (adp5585->nkeys_reset2 > 0) {
> > > + if (test_bit(info->max_rows + ADP5585_COL4, adp5585-
> > > >keypad))
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid reset2 output(C4)
> > > being used in the keypad\n");
> > > +
> > > + if (device_property_read_bool(dev, "adi,reset2-active-
> > > high"))
> > > + adp5585->reset_cfg |=
> > > FIELD_PREP(ADP5585_RESET2_POL, 1);
> > > + }
> > > +
> > > + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
> > > + return 0;
> > > +
> > > + if (device_property_read_bool(dev, "adi,rst-passtrough-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)
> > > +{
> > > + const struct adp5585_info *info = adp5585->info;
> > > + unsigned int n_pins = info->max_cols + info->max_rows;
> > > + unsigned int *keypad_pins;
> > > + unsigned int prop_val;
> > > + int n_keys, key, ret;
> > > +
> > > + adp5585->keypad = devm_bitmap_zalloc(dev, n_pins, GFP_KERNEL);
> > > + if (!adp5585->keypad)
> > > + return -ENOMEM;
> > > +
> > > + if (device_property_present(dev, "#pwm-cells"))
> > > + adp5585->has_pwm = true;
> > > +
> > > + n_keys = device_property_count_u32(dev, "adi,keypad-pins");
> > > + if (n_keys <= 0)
> > > + goto no_keypad;
> > > + if (n_keys > n_pins)
> > > + return -EINVAL;
> > > +
> > > + keypad_pins = devm_kcalloc(dev, n_keys, sizeof(*keypad_pins),
> > > + GFP_KERNEL);
> > > + if (!keypad_pins)
> > > + return -ENOMEM;
> > > +
> > > + ret = device_property_read_u32_array(dev, "adi,keypad-pins",
> > > + keypad_pins, n_keys);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + for (key = 0; key < n_keys; key++) {
> > > + if (keypad_pins[key] >= n_pins)
> > > + return -EINVAL;
> > > + if (adp5585->has_pwm && keypad_pins[key] == ADP5585_ROW3)
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid PWM pin being used in
> > > the keypad\n");
> > > + if (!info->has_row5 && keypad_pins[key] == ADP5585_ROW5)
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid row5 being used in
> > > the keypad\n");
> > > + __set_bit(keypad_pins[key], adp5585->keypad);
> > > + }
> > > +
> > > +no_keypad:
> > > + ret = device_property_read_u32(dev, "adi,key-poll-ms", &prop_val);
> > > + if (!ret) {
> > > + switch (prop_val) {
> > > + case 10:
> > > + fallthrough;
> > > + case 20:
> > > + fallthrough;
> > > + case 30:
> > > + fallthrough;
> > > + case 40:
> > > + adp5585->key_poll_time = prop_val / 10 - 1;
> > > + break;
> > > + default:
> > > + return dev_err_probe(dev, -EINVAL,
> > > + "Invalid value(%u) for
> > > adi,key-poll-ms\n",
> > > + prop_val);
> > > + }
> > > + }
> > > +
> > > + ret = adp5585_keys_unlock_parse(adp5585);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return adp5585_keys_reset_parse(adp5585);
> > > +}
> > > +
> > > +static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
> > > +{
> > > + unsigned int i;
> > > +
> > > + guard(mutex)(&adp5585->ev_lock);
> > > +
> > > + 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);
> > > +
> > > + if (key_val >= adp5585->info->gpi_ev_base &&
> > > + key_val <= adp5585->info->gpi_ev_end) {
> > > + unsigned int gpi = key_val - adp5585->info-
> > > >gpi_ev_base;
> > > +
> > > + if (adp5585->gpio_irq_handle)
> > > + adp5585->gpio_irq_handle(adp5585->gpio_dev,
> > > gpi,
> > > + key_press);
> > > + } else if (adp5585->keys_irq_handle) {
> > > + adp5585->keys_irq_handle(adp5585->input_dev,
> > > key_val,
> > > + key_press);
> > > + }
> > > + }
> > > +}
> > > +
> > > +static irqreturn_t adp5585_irq(int irq, void *data)
> > > +{
> > > + struct adp5585_dev *adp5585 = data;
> > > + unsigned int status, ev_cnt;
> > > + int ret;
> > > +
> > > + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
> > > + if (ret)
> > > + return IRQ_HANDLED;
> > > +
> > > + if (status & ADP5585_OVRFLOW_INT)
> > > + dev_err_ratelimited(adp5585->dev, "Event Overflow
> > > Error\n");
> > > +
> > > + if (!(status & ADP5585_EVENT_INT))
> > > + goto out_irq;
> > > +
> > > + ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
> > > + if (ret)
> > > + goto out_irq;
> > > +
> > > + ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
> > > + if (!ev_cnt)
> > > + goto out_irq;
> > > +
> > > + adp5585_report_events(adp5585, ev_cnt);
> > > +out_irq:
> > > + regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > static void adp5585_osc_disable(void *data)
> > > {
> > > const struct adp5585_dev *adp5585 = data;
> > > @@ -303,6 +706,128 @@ static void adp5585_osc_disable(void *data)
> > > regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
> > > }
> > >
> > > +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->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,
> > > + ®_val);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
> > > + adp5585->key_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)
> > > {
> > > const struct adp5585_info *info;
> > > @@ -321,6 +846,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > return -ENODEV;
> > >
> > > adp5585->info = info;
> > > + adp5585->dev = &i2c->dev;
> > > + adp5585->irq = i2c->irq;
> > >
> > > adp5585->regmap = devm_regmap_init_i2c(i2c, info->regmap_config);
> > > if (IS_ERR(adp5585->regmap))
> > > @@ -337,12 +864,15 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > return dev_err_probe(&i2c->dev, -ENODEV,
> > > "Invalid device ID 0x%02x\n", id);
> > >
> > > - ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
> > > - ADP5585_OSC_EN);
> > > + ret = adp5585_parse_fw(&i2c->dev, adp5585);
> > > if (ret)
> > > return ret;
> > >
> > > - ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable,
> > > adp5585);
> > > + ret = adp5585_setup(adp5585);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
> > > if (ret)
> > > return ret;
> > >
> > > @@ -353,13 +883,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;
> > > @@ -368,11 +901,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
> > > dffe1449de01dacf8fe78cf0e87d1f176d11f620..3ec542bed9c1c44899cc869d957557813e
> > > 2d0aab 100644
> > > --- a/include/linux/mfd/adp5585.h
> > > +++ b/include/linux/mfd/adp5585.h
> > > @@ -10,13 +10,21 @@
> > > #define __MFD_ADP5585_H_
> > >
> > > #include <linux/bits.h>
> > > +#include <linux/cleanup.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 +40,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
> > > @@ -60,6 +69,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
> > > @@ -104,8 +114,17 @@
> > > #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
> > >
> > > +#define ADP5585_ROW3 3
> > > +#define ADP5585_ROW4 4
> > > +#define ADP5585_ROW5 5
> > > +#define ADP5585_COL4 4
> > > +#define ADP5585_MAX_UNLOCK_TIME_SEC 7
> > > +#define ADP5585_GPI_EVENT_START 37
> > > +#define ADP5585_GPI_EVENT_END 47
> > > #define ADP5585_MAX_ROW_NUM 6
> > > #define ADP5585_MAX_COL_NUM 5
> > >
> > > @@ -124,18 +143,38 @@
> > > #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
> > > #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
> > > +#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
> > > #define ADP5589_MAX_REG ADP5589_INT_EN
> > >
> > > +#define ADP5589_GPI_EVENT_START 97
> > > +#define ADP5589_GPI_EVENT_END 115
> > > #define ADP5589_MAX_ROW_NUM 8
> > > #define ADP5589_MAX_COL_NUM 11
> > >
> > > @@ -154,11 +193,19 @@ struct adp5585_regs {
> > > unsigned int gpo_out_a;
> > > unsigned int gpio_dir_a;
> > > unsigned int gpi_stat_a;
> > > + unsigned int gpi_ev_a;
> > > + unsigned int gpi_int_lvl_a;
> > > unsigned int pwm_cfg;
> > > unsigned int pwm_offt_low;
> > > unsigned int pwm_ont_low;
> > > + unsigned int reset_cfg;
> > > unsigned int gen_cfg;
> > > unsigned int ext_cfg;
> > > + unsigned int pin_cfg_a;
> > > + unsigned int poll_ptime_cfg;
> > > + unsigned int int_en;
> > > + unsigned int reset1_event_a;
> > > + unsigned int reset2_event_a;
> > > };
> > >
> > > struct adp5585_info {
> > > @@ -169,6 +216,10 @@ struct adp5585_info {
> > > unsigned int id;
> > > u8 max_rows;
> > > u8 max_cols;
> > > + u8 gpi_ev_base;
> > > + u8 gpi_ev_end;
> > > + bool has_row5;
> > > + bool has_unlock;
> > > };
> > >
> > > struct regmap;
> > > @@ -176,6 +227,51 @@ struct regmap;
> > > struct adp5585_dev {
> > > struct regmap *regmap;
> > > const struct adp5585_info *info;
> > > + struct device *dev;
> > > + unsigned long *keypad;
> > > + void (*gpio_irq_handle)(struct device *dev, unsigned int off,
> > > + bool key_press);
> > > + struct device *gpio_dev;
> > > + void (*keys_irq_handle)(struct device *dev, unsigned int off,
> > > + bool key_press);
> > > + struct device *input_dev;
> > > + /*
> > > + * Used to synchronize usage (and availability) of
> > > gpio_irq_handle()
> > > + * and keys_irq_handle().
> > > + */
> > > + struct mutex ev_lock;
> > > + int irq;
> > > + u32 key_poll_time;
> > > + u32 unlock_time;
> > > + u32 unlock_keys[2];
> > > + u32 nkeys_unlock;
> > > + u32 reset1_keys[3];
> > > + u32 nkeys_reset1;
> > > + u32 reset2_keys[2];
> > > + u32 nkeys_reset2;
> > > + u8 reset_cfg;
> > > + bool has_pwm;
> > > };
> > >
> > > +static inline void adp5585_gpio_ev_handle_set(struct adp5585_dev *adp5585,
> > > + void (*handle)(struct device
> > > *dev,
> > > + unsigned int
> > > off,
> > > + bool
> > > key_press),
> > > + struct device *gpio_dev)
> > > +{
> > > + guard(mutex)(&adp5585->ev_lock);
> > > + adp5585->gpio_irq_handle = handle;
> > > + adp5585->gpio_dev = gpio_dev;
> > > +}
> > > +
> > > +static inline void adp5585_keys_ev_handle_set(struct adp5585_dev *adp5585,
> > > + void (*handle)(struct device
> > > *dev,
> > > + unsigned int
> > > off,
> > > + bool
> > > key_press),
> > > + struct device *input_dev)
> > > +{
> > > + guard(mutex)(&adp5585->ev_lock);
> > > + adp5585->keys_irq_handle = handle;
> > > + adp5585->input_dev = input_dev;
> > > +}
> > > #endif
> > >
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe
2025-04-21 22:03 ` Laurent Pinchart
@ 2025-04-22 7:50 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-22 7:50 UTC (permalink / raw)
To: Laurent Pinchart
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,
Dmitry Torokhov, Liu Ying
On Tue, 2025-04-22 at 01:03 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> On Mon, Apr 21, 2025 at 01:14:28PM +0100, Nuno Sá wrote:
> > On Mon, 2025-04-21 at 11:57 +0300, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:18PM +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).
> > >
> > > Setting this bit unconditionally increases power consumption. It should
> > > only be set when needed.
> >
> > Well, not sure if the effort for that pays off... The only usecase were it
> > would
> > make sense to do that would be for PWM. For the other devices (and assuming
> > I'm
> > right with the GPI case) we need this always set.
>
> For the keypad, can't the device be kept powered off if the input device
> exposed to userspace is not open ? And for GPIOs, OSC_EN isn't needed
> when all requested GPIOs are configured as outputs, as far as I can
> tell.
Yes, I do know it's doable (well, TBH for the input case I just learned we can
define .open()/.close() callbacks). My point was just this adds some complexity
and I'm not sure of the added value (while saving power is always nice)
>
> I'm fine addressing this issue on top of this series.
Agreed. I would prefer that. This series is already big enough.
>
> > > > 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
> > > > 160e0b38106a6d78f7d4b7c866cb603d96ea673e..f17b5f2474cac6a403556694066f43
> > > > 8288
> > > > 264a49 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -110,6 +110,13 @@ static const struct regmap_config
> > > > adp5585_regmap_configs[] = {
> > > > },
> > > > };
> > > >
> > > > +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;
> > > > @@ -138,6 +145,15 @@ static int adp5585_i2c_probe(struct i2c_client
> > > > *i2c)
> > > > return dev_err_probe(&i2c->dev, -ENODEV,
> > > > "Invalid device ID 0x%02x\n", id);
> > > >
> > > > + 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,
> > > > adp5585_devs,
> > > > ARRAY_SIZE(adp5585_devs),
> > > > NULL, 0, NULL);
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-21 22:06 ` Laurent Pinchart
@ 2025-04-22 7:51 ` Nuno Sá
2025-04-24 16:01 ` Lee Jones
1 sibling, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-22 7:51 UTC (permalink / raw)
To: Laurent Pinchart
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,
Dmitry Torokhov, Liu Ying
On Tue, 2025-04-22 at 01:06 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> On Mon, Apr 21, 2025 at 01:21:08PM +0100, Nuno Sá wrote:
> > On Mon, 2025-04-21 at 12:15 +0300, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:22PM +0100, Nuno Sá via B4 Relay wrote:
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix
> > > > decoder,
> > > > programmable logic, reset generator, and PWM generator.
> > > >
> > > > This patch adds the foundation to add support for the adp5589 gpio and
> > > > pwm
> > > > drivers. Most importantly, we need to differentiate between some
> > > > registers addresses. It also hints to future keymap support.
> > >
> > > Please split this in two patches, one that reworks the driver to support
> > > different register addresses, and one that adds adp5589 support.
> > >
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 223
> > > > +++++++++++++++++++++++++++++++++++++++++---
> > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > >
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index
> > > > fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d9466799341061
> > > > 0be7fc3672 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
> > > >
> > > > };
> > > >
> > > > +static const struct mfd_cell adp5589_devs[] = {
> > > > + MFD_CELL_NAME("adp5589-keys"),
> > > > + 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),
> > > > };
> > > > @@ -34,6 +41,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:
> > > > @@ -77,10 +93,52 @@ 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,
> > > > +};
> > > > +
> > > > enum adp5585_regmap_type {
> > > > ADP5585_REGMAP_00,
> > > > ADP5585_REGMAP_02,
> > > > ADP5585_REGMAP_04,
> > > > + ADP5589_REGMAP_00,
> > > > + ADP5589_REGMAP_01,
> > > > + ADP5589_REGMAP_02,
> > > > };
> > > >
> > > > static const struct regmap_config adp5585_regmap_configs[] = {
> > > > @@ -111,6 +169,131 @@ static const struct regmap_config
> > > > adp5585_regmap_configs[] = {
> > > > .reg_defaults_raw = adp5585_regmap_defaults_04,
> > > > .num_reg_defaults_raw =
> > > > sizeof(adp5585_regmap_defaults_04),
> > > > },
> > > > + [ADP5589_REGMAP_00] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_00,
> > > > + .num_reg_defaults_raw =
> > > > sizeof(adp5589_regmap_defaults_00),
> > > > + },
> > > > + [ADP5589_REGMAP_01] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_01,
> > > > + .num_reg_defaults_raw =
> > > > sizeof(adp5589_regmap_defaults_01),
> > > > + },
> > > > + [ADP5589_REGMAP_02] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_02,
> > > > + .num_reg_defaults_raw =
> > > > sizeof(adp5589_regmap_defaults_02),
> > > > + },
> > > > +};
> > > > +
> > > > +static const struct adp5585_regs adp5585_regs = {
> > > > + .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,
> > > > + .pwm_cfg = ADP5585_PWM_CFG,
> > > > + .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > > > + .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > > > + .gen_cfg = ADP5585_GENERAL_CFG,
> > > > + .ext_cfg = ADP5585_PIN_CONFIG_C,
> > > > +};
> > >
> > > Why does this need to be stored in this driver, and not in the drivers
> > > for the gpio and pwm cells ? If the kernel is compiled without e.g. the
> > > adp5585-pwm driver, we shouldn't waste memory here by adding data that
> > > only the adp5585-pwm driver needs.
> >
> > I don't really think the memory we would save to be that relevant but I can
> > better separate things. I guess i went like this because there's some shared
> > variables that will have to be in the top level structs and I did not wanted
> > to
> > have a "global" and "local" regs thingy...
>
> I understand, and I think it's at least partly a coding style
> preference. Personally, I find that having child-specific data in child
> drivers makes the code easier to read, as it increases locality.
> Otherwise, I have to look through multiple child drivers to see if and
> where each field is used.
No strong feelings on my side. So, I'll give this a shot in v3
>
> > > > +
> > > > +static const struct adp5585_regs adp5589_regs = {
> > > > + .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,
> > > > + .pwm_cfg = ADP5589_PWM_CFG,
> > > > + .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
> > > > + .pwm_ont_low = ADP5589_PWM_ONT_LOW,
> > > > + .gen_cfg = ADP5589_GENERAL_CFG,
> > > > + .ext_cfg = ADP5589_PIN_CONFIG_D,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5585_info = {
> > > > + .adp5585_devs = adp5585_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > > + .id = ADP5585_MAN_ID_VALUE,
> > > > + .regs = &adp5585_regs,
> > > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > > + .max_cols = ADP5585_MAX_COL_NUM,
> > >
> > > Same here, the max_rows and max_cols fields don't seem to belong to this
> > > driver.
> > >
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5585_01_info = {
> > > > + .adp5585_devs = adp5585_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > > + .id = ADP5585_MAN_ID_VALUE,
> > > > + .regs = &adp5585_regs,
> > > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5585_02_info = {
> > > > + .adp5585_devs = adp5585_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_02],
> > > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > > + .id = ADP5585_MAN_ID_VALUE,
> > > > + .regs = &adp5585_regs,
> > > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5585_04_info = {
> > > > + .adp5585_devs = adp5585_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5585_REGMAP_04],
> > > > + .n_devs = ARRAY_SIZE(adp5585_devs),
> > > > + .id = ADP5585_MAN_ID_VALUE,
> > > > + .regs = &adp5585_regs,
> > > > + .max_rows = ADP5585_MAX_ROW_NUM,
> > > > + .max_cols = ADP5585_MAX_COL_NUM,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5589_info = {
> > > > + .adp5585_devs = adp5589_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_00],
> > > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > > + .id = ADP5589_MAN_ID_VALUE,
> > > > + .regs = &adp5589_regs,
> > > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5589_01_info = {
> > > > + .adp5585_devs = adp5589_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_01],
> > > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > > + .id = ADP5589_MAN_ID_VALUE,
> > > > + .regs = &adp5589_regs,
> > > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > > +};
> > > > +
> > > > +static const struct adp5585_info adp5589_02_info = {
> > > > + .adp5585_devs = adp5589_devs,
> > > > + .regmap_config = &adp5585_regmap_configs[ADP5589_REGMAP_02],
> > > > + .n_devs = ARRAY_SIZE(adp5589_devs),
> > > > + .id = ADP5589_MAN_ID_VALUE,
> > > > + .regs = &adp5589_regs,
> > > > + .max_rows = ADP5589_MAX_ROW_NUM,
> > > > + .max_cols = ADP5589_MAX_COL_NUM,
> > > > };
> > > >
> > > > static void adp5585_osc_disable(void *data)
> > > > @@ -122,7 +305,7 @@ static void adp5585_osc_disable(void *data)
> > > >
> > > > static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > > {
> > > > - const struct regmap_config *regmap_config;
> > > > + const struct adp5585_info *info;
> > > > struct adp5585_dev *adp5585;
> > > > unsigned int id;
> > > > int ret;
> > > > @@ -133,8 +316,13 @@ static int adp5585_i2c_probe(struct i2c_client
> > > > *i2c)
> > > >
> > > > i2c_set_clientdata(i2c, adp5585);
> > > >
> > > > - regmap_config = i2c_get_match_data(i2c);
> > > > - adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
> > > > + info = i2c_get_match_data(i2c);
> > > > + if (!info)
> > > > + return -ENODEV;
> > >
> > > Can this happen ?
> > >
> > > > +
> > > > + adp5585->info = info;
> > >
> > > Drop the local info variable and assign the value to adp5585->info
> > > directly.
> > >
> > > > +
> > > > + adp5585->regmap = devm_regmap_init_i2c(i2c, info-
> > > > >regmap_config);
> > > > if (IS_ERR(adp5585->regmap))
> > > > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585-
> > > > >regmap),
> > > > "Failed to initialize register
> > > > map\n");
> > > > @@ -144,7 +332,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);
> > > >
> > > > @@ -158,8 +347,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
> > > > return ret;
> > > >
> > > > ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> > > > - adp5585_devs,
> > > > ARRAY_SIZE(adp5585_devs),
> > > > - NULL, 0, NULL);
> > > > + adp5585->info->adp5585_devs,
> > > > + adp5585->info->n_devs, NULL, 0,
> > > > NULL);
> > > > if (ret)
> > > > return dev_err_probe(&i2c->dev, ret,
> > > > "Failed to add child devices\n");
> > > > @@ -191,19 +380,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm,
> > > > adp5585_suspend, adp5585_resume);
> > > > static const struct of_device_id adp5585_of_match[] = {
> > > > {
> > > > .compatible = "adi,adp5585-00",
> > > > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > > + .data = &adp5585_info,
> > > > }, {
> > > > .compatible = "adi,adp5585-01",
> > > > - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
> > > > + .data = &adp5585_01_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,
> > > > + }, {
> > > > + .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
> > > > 016033cd68e46757aca86d21dd37025fd354b801..dffe1449de01dacf8fe78cf0e87d1f
> > > > 176d
> > > > 11f620 100644
> > > > --- a/include/linux/mfd/adp5585.h
> > > > +++ b/include/linux/mfd/adp5585.h
> > > > @@ -104,9 +104,11 @@
> > > > #define ADP5585_INT_CFG BIT(1)
> > > > #define ADP5585_RST_CFG BIT(0)
> > > > #define ADP5585_INT_EN 0x3c
> > > > -
> > > > #define ADP5585_MAX_REG ADP5585_INT_EN
> > > >
> > > > +#define ADP5585_MAX_ROW_NUM 6
> > > > +#define ADP5585_MAX_COL_NUM 5
> > > > +
> > > > /*
> > > > * 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
> > > > @@ -117,10 +119,63 @@
> > > > #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_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
> > >
> > > Indentation looks wrong.
> > >
> > > > +#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_GENERAL_CFG 0x4d
> > > > +#define ADP5589_INT_EN 0x4e
> > > > +#define ADP5589_MAX_REG ADP5589_INT_EN
> > > > +
> > > > +#define ADP5589_MAX_ROW_NUM 8
> > > > +#define ADP5589_MAX_COL_NUM 11
> > > > +
> > > > +/*
> > > > + * 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_regs {
> > > > + 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 pwm_cfg;
> > > > + unsigned int pwm_offt_low;
> > > > + unsigned int pwm_ont_low;
> > > > + unsigned int gen_cfg;
> > > > + unsigned int ext_cfg;
> > > > +};
> > > > +
> > > > +struct adp5585_info {
> > > > + const struct mfd_cell *adp5585_devs;
> > > > + const struct regmap_config *regmap_config;
> > > > + const struct adp5585_regs *regs;
> > > > + unsigned int n_devs;
> > > > + unsigned int id;
> > > > + u8 max_rows;
> > > > + u8 max_cols;
> > > > +};
> > > > +
> > > > struct regmap;
> > > >
> > > > struct adp5585_dev {
> > > > struct regmap *regmap;
> > > > + const struct adp5585_info *info;
> > > > };
> > > >
> > > > #endif
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator
2025-04-21 22:09 ` Laurent Pinchart
@ 2025-04-22 8:12 ` Nuno Sá
0 siblings, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-22 8:12 UTC (permalink / raw)
To: Laurent Pinchart
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,
Dmitry Torokhov, Liu Ying
On Tue, 2025-04-22 at 01:09 +0300, Laurent Pinchart wrote:
> Hi Nuno,
>
> On Mon, Apr 21, 2025 at 01:38:47PM +0100, Nuno Sá wrote:
> > On Mon, 2025-04-21 at 12:48 +0300, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:30PM +0100, Nuno Sá via B4 Relay wrote:
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > Make sure we get and enable the VDD supply (if available).
> > >
> > > Can the regulator be enabled only when needed ?
> > >
> >
> > Hmm, I guess we could do that for the case where only the PWM device is
> > "enabled". That said, I don't think the extra complexity for that really
> > pays
> > off...
>
> Or when no GPIO is requested, or when the input device for the keypad is
> not open ? Then can be handled with power optimization for OSC_EN on top
> of this series.
>
I guess it can go sideways with enabling/disabling OSC_EN, yes...
> > > > 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
> > > > c1d51d50dca6c9367d4a1b98a4f8bbec12dbf90b..667cc5bd0745f64eec60837ec3c000
> > > > 57af
> > > > 0cddeb 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -18,6 +18,7 @@
> > > > #include <linux/mod_devicetable.h>
> > > > #include <linux/module.h>
> > > > #include <linux/regmap.h>
> > > > +#include <linux/regulator/consumer.h>
> > > > #include <linux/types.h>
> > > >
> > > > static const struct mfd_cell adp5585_devs[] = {
> > > > @@ -849,6 +850,10 @@ static int adp5585_i2c_probe(struct i2c_client
> > > > *i2c)
> > > > adp5585->dev = &i2c->dev;
> > > > adp5585->irq = i2c->irq;
> > > >
> > > > + ret = devm_regulator_get_enable(&i2c->dev, "vdd");
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > adp5585->regmap = devm_regmap_init_i2c(i2c, info-
> > > > >regmap_config);
> > > > if (IS_ERR(adp5585->regmap))
> > > > return dev_err_probe(&i2c->dev, PTR_ERR(adp5585-
> > > > >regmap),
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME()
2025-04-15 14:49 ` [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
2025-04-21 9:03 ` Laurent Pinchart
@ 2025-04-24 15:55 ` Lee Jones
1 sibling, 0 replies; 81+ messages in thread
From: Lee Jones @ 2025-04-24 15:55 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Use the helper macro. No functional change intended...
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> index f17b5f2474cac6a403556694066f438288264a49..fafe3ad93ea196e1eb8e79fecba58f36f12167eb 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>
> @@ -18,8 +19,10 @@
> #include <linux/types.h>
>
> static const struct mfd_cell adp5585_devs[] = {
> - { .name = "adp5585-gpio", },
> - { .name = "adp5585-pwm", },
> + MFD_CELL_NAME("adp5585-keys"),
This looks like a functional change to me?
> + MFD_CELL_NAME("adp5585-gpio"),
> + MFD_CELL_NAME("adp5585-pwm"),
> +
Superfluous blank line.
> };
>
> static const struct regmap_range adp5585_volatile_ranges[] = {
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-21 22:06 ` Laurent Pinchart
2025-04-22 7:51 ` Nuno Sá
@ 2025-04-24 16:01 ` Lee Jones
1 sibling, 0 replies; 81+ messages in thread
From: Lee Jones @ 2025-04-24 16:01 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, 22 Apr 2025, Laurent Pinchart wrote:
> Hi Nuno,
>
> On Mon, Apr 21, 2025 at 01:21:08PM +0100, Nuno Sá wrote:
> > On Mon, 2025-04-21 at 12:15 +0300, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:22PM +0100, Nuno Sá via B4 Relay wrote:
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > > programmable logic, reset generator, and PWM generator.
> > > >
> > > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > > drivers. Most importantly, we need to differentiate between some
> > > > registers addresses. It also hints to future keymap support.
> > >
> > > Please split this in two patches, one that reworks the driver to support
> > > different register addresses, and one that adds adp5589 support.
> > >
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > >
> > > > diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
> > > > index fafe3ad93ea196e1eb8e79fecba58f36f12167eb..c3586c0d6aa2e7e7d94667993410610be7fc3672 100644
> > > > --- a/drivers/mfd/adp5585.c
> > > > +++ b/drivers/mfd/adp5585.c
> > > > @@ -25,6 +25,13 @@ static const struct mfd_cell adp5585_devs[] = {
> > > >
> > > > };
> > > >
> > > > +static const struct mfd_cell adp5589_devs[] = {
> > > > + MFD_CELL_NAME("adp5589-keys"),
> > > > + 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),
> > > > };
> > > > @@ -34,6 +41,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:
> > > > @@ -77,10 +93,52 @@ 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,
> > > > +};
> > > > +
> > > > enum adp5585_regmap_type {
> > > > ADP5585_REGMAP_00,
> > > > ADP5585_REGMAP_02,
> > > > ADP5585_REGMAP_04,
> > > > + ADP5589_REGMAP_00,
> > > > + ADP5589_REGMAP_01,
> > > > + ADP5589_REGMAP_02,
> > > > };
> > > >
> > > > static const struct regmap_config adp5585_regmap_configs[] = {
> > > > @@ -111,6 +169,131 @@ static const struct regmap_config adp5585_regmap_configs[] = {
> > > > .reg_defaults_raw = adp5585_regmap_defaults_04,
> > > > .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
> > > > },
> > > > + [ADP5589_REGMAP_00] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_00,
> > > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_00),
> > > > + },
> > > > + [ADP5589_REGMAP_01] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_01,
> > > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_01),
> > > > + },
> > > > + [ADP5589_REGMAP_02] = {
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > > > + .max_register = ADP5589_MAX_REG,
> > > > + .volatile_table = &adp5589_volatile_regs,
> > > > + .cache_type = REGCACHE_MAPLE,
> > > > + .reg_defaults_raw = adp5589_regmap_defaults_02,
> > > > + .num_reg_defaults_raw = sizeof(adp5589_regmap_defaults_02),
> > > > + },
> > > > +};
> > > > +
> > > > +static const struct adp5585_regs adp5585_regs = {
> > > > + .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,
> > > > + .pwm_cfg = ADP5585_PWM_CFG,
> > > > + .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
> > > > + .pwm_ont_low = ADP5585_PWM_ONT_LOW,
> > > > + .gen_cfg = ADP5585_GENERAL_CFG,
> > > > + .ext_cfg = ADP5585_PIN_CONFIG_C,
> > > > +};
> > >
> > > Why does this need to be stored in this driver, and not in the drivers
> > > for the gpio and pwm cells ? If the kernel is compiled without e.g. the
> > > adp5585-pwm driver, we shouldn't waste memory here by adding data that
> > > only the adp5585-pwm driver needs.
> >
> > I don't really think the memory we would save to be that relevant but I can
> > better separate things. I guess i went like this because there's some shared
> > variables that will have to be in the top level structs and I did not wanted to
> > have a "global" and "local" regs thingy...
Memory and power savings are very important.
Please prioritise them over effort.
> I understand, and I think it's at least partly a coding style
> preference. Personally, I find that having child-specific data in child
> drivers makes the code easier to read, as it increases locality.
> Otherwise, I have to look through multiple child drivers to see if and
> where each field is used.
Right. If resources are only used in specific sub-devices, please move
it there. Only shared resources should be handed in the parent.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-21 9:33 ` Laurent Pinchart
2025-04-21 12:32 ` Nuno Sá
@ 2025-04-24 16:07 ` Lee Jones
2025-04-24 16:24 ` Laurent Pinchart
1 sibling, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-04-24 16:07 UTC (permalink / raw)
To: Laurent Pinchart
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 Mon, 21 Apr 2025, Laurent Pinchart wrote:
> Hi Nuno,
>
> Thank you for the patch.
>
> On Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > This adds support for key events. Basically, it adds support for all the
> > FW parsing and configuration of the keys (also making sure there's no
> > overlaps). They can either be part of a matrix keymap (in which case
> > events will be handled by an input device) or they can be gpi's (in which
> > case events will be handled by the gpiochip device via gpio_keys). There's
> > also support for unlock keys as for reset keys.
>
> There's lots of code below specific to matrix keypad handling. Please
> move it to the keypad driver, and keep this driver as lean as possible.
Exactly this. MFD shouldn't need to contain anything that should be
handed in the leaf drivers. Keypad handling should be processed by
keypad driver, etc. If the GPIO driver needs to have visibility into
keypad API, add some exported helpers in the Keyoad driver.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-15 14:49 ` [PATCH v2 06/17] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
2025-04-21 9:15 ` Laurent Pinchart
@ 2025-04-24 16:18 ` Lee Jones
2025-04-24 16:30 ` Laurent Pinchart
1 sibling, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-04-24 16:18 UTC (permalink / raw)
To: nuno.sa
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Laurent Pinchart, Liu Ying
On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> programmable logic, reset generator, and PWM generator.
>
> This patch adds the foundation to add support for the adp5589 gpio and pwm
> drivers. Most importantly, we need to differentiate between some
> registers addresses. It also hints to future keymap support.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> include/linux/mfd/adp5585.h | 57 ++++++++++-
> 2 files changed, 268 insertions(+), 12 deletions(-)
[...]
> + * 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_regs {
> + 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 pwm_cfg;
> + unsigned int pwm_offt_low;
> + unsigned int pwm_ont_low;
> + unsigned int gen_cfg;
> + unsigned int ext_cfg;
> +};
> +
> +struct adp5585_info {
> + const struct mfd_cell *adp5585_devs;
Okay, we are never doing this. Either use OF for platform registration
or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
> + const struct regmap_config *regmap_config;
> + const struct adp5585_regs *regs;
> + unsigned int n_devs;
> + unsigned int id;
What ID is this? We already have platform IDs and MFD cell IDs.
> + u8 max_rows;
> + u8 max_cols;
> +};
> +
> struct regmap;
>
> struct adp5585_dev {
> struct regmap *regmap;
> + const struct adp5585_info *info;
> };
>
> #endif
>
> --
> 2.49.0
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-24 16:07 ` Lee Jones
@ 2025-04-24 16:24 ` Laurent Pinchart
2025-04-24 16:28 ` Lee Jones
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-24 16:24 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
Hi Lee,
On Thu, Apr 24, 2025 at 05:07:06PM +0100, Lee Jones wrote:
> On Mon, 21 Apr 2025, Laurent Pinchart wrote:
> > On Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > This adds support for key events. Basically, it adds support for all the
> > > FW parsing and configuration of the keys (also making sure there's no
> > > overlaps). They can either be part of a matrix keymap (in which case
> > > events will be handled by an input device) or they can be gpi's (in which
> > > case events will be handled by the gpiochip device via gpio_keys). There's
> > > also support for unlock keys as for reset keys.
> >
> > There's lots of code below specific to matrix keypad handling. Please
> > move it to the keypad driver, and keep this driver as lean as possible.
>
> Exactly this. MFD shouldn't need to contain anything that should be
> handed in the leaf drivers. Keypad handling should be processed by
> keypad driver, etc. If the GPIO driver needs to have visibility into
> keypad API, add some exported helpers in the Keyoad driver.
To give some context, the ADP5585 is an exotic device. It has this
feature where different functions (keypad, GPIO, ...) can generate
events that are all pushed to a single hardware FIFO. The driver reads
events from the FIFO, and processes them accordingly to the event type.
Handling the FIFO and reading the events doesn't belong to any
particular leaf driver, I think it makes sense to keep that in the main
driver. Processing of the events after dispatching based on the type,
however, should belong to leaf drivers. I'd like to see less
keypad-specific code in the main driver, but some additions are likely
still required.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 10/17] mfd: adp5585: add support for key events
2025-04-24 16:24 ` Laurent Pinchart
@ 2025-04-24 16:28 ` Lee Jones
0 siblings, 0 replies; 81+ messages in thread
From: Lee Jones @ 2025-04-24 16:28 UTC (permalink / raw)
To: Laurent Pinchart
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 Thu, 24 Apr 2025, Laurent Pinchart wrote:
> Hi Lee,
>
> On Thu, Apr 24, 2025 at 05:07:06PM +0100, Lee Jones wrote:
> > On Mon, 21 Apr 2025, Laurent Pinchart wrote:
> > > On Tue, Apr 15, 2025 at 03:49:26PM +0100, Nuno Sá via B4 Relay wrote:
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > This adds support for key events. Basically, it adds support for all the
> > > > FW parsing and configuration of the keys (also making sure there's no
> > > > overlaps). They can either be part of a matrix keymap (in which case
> > > > events will be handled by an input device) or they can be gpi's (in which
> > > > case events will be handled by the gpiochip device via gpio_keys). There's
> > > > also support for unlock keys as for reset keys.
> > >
> > > There's lots of code below specific to matrix keypad handling. Please
> > > move it to the keypad driver, and keep this driver as lean as possible.
> >
> > Exactly this. MFD shouldn't need to contain anything that should be
> > handed in the leaf drivers. Keypad handling should be processed by
> > keypad driver, etc. If the GPIO driver needs to have visibility into
> > keypad API, add some exported helpers in the Keypad driver.
>
> To give some context, the ADP5585 is an exotic device. It has this
> feature where different functions (keypad, GPIO, ...) can generate
> events that are all pushed to a single hardware FIFO. The driver reads
> events from the FIFO, and processes them accordingly to the event type.
> Handling the FIFO and reading the events doesn't belong to any
> particular leaf driver, I think it makes sense to keep that in the main
> driver. Processing of the events after dispatching based on the type,
> however, should belong to leaf drivers. I'd like to see less
> keypad-specific code in the main driver, but some additions are likely
> still required.
Understood. Let's shift out what we can though.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-24 16:18 ` Lee Jones
@ 2025-04-24 16:30 ` Laurent Pinchart
2025-04-24 16:38 ` Lee Jones
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-24 16:30 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 Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
>
> > From: Nuno Sá <nuno.sa@analog.com>
> >
> > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > programmable logic, reset generator, and PWM generator.
> >
> > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > drivers. Most importantly, we need to differentiate between some
> > registers addresses. It also hints to future keymap support.
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
> > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > 2 files changed, 268 insertions(+), 12 deletions(-)
>
> [...]
>
> > + * 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_regs {
> > + 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 pwm_cfg;
> > + unsigned int pwm_offt_low;
> > + unsigned int pwm_ont_low;
> > + unsigned int gen_cfg;
> > + unsigned int ext_cfg;
> > +};
> > +
> > +struct adp5585_info {
> > + const struct mfd_cell *adp5585_devs;
>
> Okay, we are never doing this. Either use OF for platform registration
> or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
When I upstreamed the initial driver, I modelled the different functions
through child nodes in DT, with a compatible string for each child. I
was told very strongly to remove that. We have therefore no other choice
than constructing the name of the cells based on the model of the main
device.
> > + const struct regmap_config *regmap_config;
> > + const struct adp5585_regs *regs;
> > + unsigned int n_devs;
> > + unsigned int id;
>
> What ID is this? We already have platform IDs and MFD cell IDs.
That's the value of the hardware model ID read-only register, it is used
as a safety check to verify that the connected device corresponds to the
compatible string.
> > + u8 max_rows;
> > + u8 max_cols;
> > +};
> > +
> > struct regmap;
> >
> > struct adp5585_dev {
> > struct regmap *regmap;
> > + const struct adp5585_info *info;
> > };
> >
> > #endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-24 16:30 ` Laurent Pinchart
@ 2025-04-24 16:38 ` Lee Jones
2025-04-24 19:39 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-04-24 16:38 UTC (permalink / raw)
To: Laurent Pinchart
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 Thu, 24 Apr 2025, Laurent Pinchart wrote:
> On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> >
> > > From: Nuno Sá <nuno.sa@analog.com>
> > >
> > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > programmable logic, reset generator, and PWM generator.
> > >
> > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > drivers. Most importantly, we need to differentiate between some
> > > registers addresses. It also hints to future keymap support.
> > >
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > 2 files changed, 268 insertions(+), 12 deletions(-)
> >
> > [...]
> >
> > > + * 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_regs {
> > > + 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 pwm_cfg;
> > > + unsigned int pwm_offt_low;
> > > + unsigned int pwm_ont_low;
> > > + unsigned int gen_cfg;
> > > + unsigned int ext_cfg;
> > > +};
> > > +
> > > +struct adp5585_info {
> > > + const struct mfd_cell *adp5585_devs;
> >
> > Okay, we are never doing this. Either use OF for platform registration
> > or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
>
> When I upstreamed the initial driver, I modelled the different functions
> through child nodes in DT, with a compatible string for each child. I
> was told very strongly to remove that. We have therefore no other choice
> than constructing the name of the cells based on the model of the main
> device.
It's okay to add this information statically in this driver. It's not
okay to then pass it through the OF API. You can pass an identifier
through the .data attribute to match on, but we are not passing MFD cell
data through like this.
> > > + const struct regmap_config *regmap_config;
> > > + const struct adp5585_regs *regs;
> > > + unsigned int n_devs;
> > > + unsigned int id;
> >
> > What ID is this? We already have platform IDs and MFD cell IDs.
>
> That's the value of the hardware model ID read-only register, it is used
> as a safety check to verify that the connected device corresponds to the
> compatible string.
I suggest changing the nomenclature to be more forthcoming.
'model', 'version', 'hwid', 'chipid', etc.
Why is it being stored? Is it used to match on at a later date?
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-24 16:38 ` Lee Jones
@ 2025-04-24 19:39 ` Laurent Pinchart
2025-04-25 7:58 ` Lee Jones
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-24 19:39 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 Thu, Apr 24, 2025 at 05:38:30PM +0100, Lee Jones wrote:
> On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> > >
> > > > From: Nuno Sá <nuno.sa@analog.com>
> > > >
> > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > > programmable logic, reset generator, and PWM generator.
> > > >
> > > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > > drivers. Most importantly, we need to differentiate between some
> > > > registers addresses. It also hints to future keymap support.
> > > >
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > ---
> > > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > >
> > > [...]
> > >
> > > > + * 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_regs {
> > > > + 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 pwm_cfg;
> > > > + unsigned int pwm_offt_low;
> > > > + unsigned int pwm_ont_low;
> > > > + unsigned int gen_cfg;
> > > > + unsigned int ext_cfg;
> > > > +};
> > > > +
> > > > +struct adp5585_info {
> > > > + const struct mfd_cell *adp5585_devs;
> > >
> > > Okay, we are never doing this. Either use OF for platform registration
> > > or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
> >
> > When I upstreamed the initial driver, I modelled the different functions
> > through child nodes in DT, with a compatible string for each child. I
> > was told very strongly to remove that. We have therefore no other choice
> > than constructing the name of the cells based on the model of the main
> > device.
>
> It's okay to add this information statically in this driver. It's not
> okay to then pass it through the OF API. You can pass an identifier
> through the .data attribute to match on, but we are not passing MFD cell
> data through like this.
Sorry, I'm not following you. What's the issue with the .data field
pointing to an instance of a structure that lists properties related to
the device model ?
> > > > + const struct regmap_config *regmap_config;
> > > > + const struct adp5585_regs *regs;
> > > > + unsigned int n_devs;
> > > > + unsigned int id;
> > >
> > > What ID is this? We already have platform IDs and MFD cell IDs.
> >
> > That's the value of the hardware model ID read-only register, it is used
> > as a safety check to verify that the connected device corresponds to the
> > compatible string.
>
> I suggest changing the nomenclature to be more forthcoming.
>
> 'model', 'version', 'hwid', 'chipid', etc.
>
> Why is it being stored? Is it used to match on at a later date?
The adp5585_info structure contains static information the describe each
device model. There's one global static const instance per device model,
and they are referenced from device id structures (e.g. of_device_id).
The driver gets an info pointer corresponding to the model reported by
the platform firmware (e.g. DT). It reads the device ID from the device
at probe time, and compares it with the value stored in the structure as
a safety check to ensure there's no mismatch.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-24 19:39 ` Laurent Pinchart
@ 2025-04-25 7:58 ` Lee Jones
2025-04-25 9:13 ` Laurent Pinchart
0 siblings, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-04-25 7:58 UTC (permalink / raw)
To: Laurent Pinchart
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 Thu, 24 Apr 2025, Laurent Pinchart wrote:
> On Thu, Apr 24, 2025 at 05:38:30PM +0100, Lee Jones wrote:
> > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > > > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> > > >
> > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > >
> > > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > > > programmable logic, reset generator, and PWM generator.
> > > > >
> > > > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > > > drivers. Most importantly, we need to differentiate between some
> > > > > registers addresses. It also hints to future keymap support.
> > > > >
> > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > ---
> > > > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > >
> > > > [...]
> > > >
> > > > > + * 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_regs {
> > > > > + 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 pwm_cfg;
> > > > > + unsigned int pwm_offt_low;
> > > > > + unsigned int pwm_ont_low;
> > > > > + unsigned int gen_cfg;
> > > > > + unsigned int ext_cfg;
> > > > > +};
> > > > > +
> > > > > +struct adp5585_info {
> > > > > + const struct mfd_cell *adp5585_devs;
> > > >
> > > > Okay, we are never doing this. Either use OF for platform registration
> > > > or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
> > >
> > > When I upstreamed the initial driver, I modelled the different functions
> > > through child nodes in DT, with a compatible string for each child. I
> > > was told very strongly to remove that. We have therefore no other choice
> > > than constructing the name of the cells based on the model of the main
> > > device.
> >
> > It's okay to add this information statically in this driver. It's not
> > okay to then pass it through the OF API. You can pass an identifier
> > through the .data attribute to match on, but we are not passing MFD cell
> > data through like this.
>
> Sorry, I'm not following you. What's the issue with the .data field
> pointing to an instance of a structure that lists properties related to
> the device model ?
There isn't one. By all means place any type of platform data you want
in there. Similar to the information you'd find in Device Tree or the
old board-files type pdata. You can even extract the platform data you
pass through the OF API and place it into MFD platform data. The line
is being drawn on passing through one type of initialisation API with
another, MFD through OF in this case. MFD cells containing device
registration data (including platform data!) is not itself platform
data.
> > > > > + const struct regmap_config *regmap_config;
> > > > > + const struct adp5585_regs *regs;
> > > > > + unsigned int n_devs;
> > > > > + unsigned int id;
> > > >
> > > > What ID is this? We already have platform IDs and MFD cell IDs.
> > >
> > > That's the value of the hardware model ID read-only register, it is used
> > > as a safety check to verify that the connected device corresponds to the
> > > compatible string.
> >
> > I suggest changing the nomenclature to be more forthcoming.
> >
> > 'model', 'version', 'hwid', 'chipid', etc.
> >
> > Why is it being stored? Is it used to match on at a later date?
>
> The adp5585_info structure contains static information the describe each
> device model. There's one global static const instance per device model,
> and they are referenced from device id structures (e.g. of_device_id).
> The driver gets an info pointer corresponding to the model reported by
> the platform firmware (e.g. DT). It reads the device ID from the device
> at probe time, and compares it with the value stored in the structure as
> a safety check to ensure there's no mismatch.
I think the current implementation (as a whole, not just the IDs) needs
a rethink. Very few attributes are changing here, both between the 2
platforms and the several variants you're trying to support, leading to
masses of repetition.
Looking at the static configuration here, this is starting to look like
2 pieces of hardware with the only variation within each being the
default register values. Is that a correct assumption? If so, does
mean all of this added complexity is just to configure a few register
values such that the two platforms can be used for different things? Or
are these really 6 true hardware variants of one another?
Either way, this approach doesn't scale. Instead of multiplying the
amount of platforms / variants together and creating that number of
static structs, I'd suggest using templating and only adapting what
actually changes.
For instance, the following attributes in 'struct regmap_config' never
change; reg_bits, val_bits, and cache_type. And max_register only
changes between the 2 hardware platforms. The reg_defaults_raw values
can be changed in a switch statement.
Same goes for 'struct adp5585_info'. Only regmap_config changes between
variants. Everything else is exactly the same. So, with the use of a
few of templates and a couple of succinct switch cases, you can control
all of the differentiation you need. And for every variant you wish to
add, it's a couple of extra lines rather than many, leading to a
much more scaleable implementation.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-25 7:58 ` Lee Jones
@ 2025-04-25 9:13 ` Laurent Pinchart
2025-04-25 9:23 ` Lee Jones
2025-04-28 9:24 ` Nuno Sá
0 siblings, 2 replies; 81+ messages in thread
From: Laurent Pinchart @ 2025-04-25 9:13 UTC (permalink / raw)
To: Lee Jones
Cc: nuno.sa, linux-gpio, linux-pwm, devicetree, linux-input,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Liu Ying
Hi Lee,
On Fri, Apr 25, 2025 at 08:58:59AM +0100, Lee Jones wrote:
> On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > On Thu, Apr 24, 2025 at 05:38:30PM +0100, Lee Jones wrote:
> > > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > > On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > > > > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> > > > >
> > > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > > >
> > > > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > > > > programmable logic, reset generator, and PWM generator.
> > > > > >
> > > > > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > > > > drivers. Most importantly, we need to differentiate between some
> > > > > > registers addresses. It also hints to future keymap support.
> > > > > >
> > > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > > ---
> > > > > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > > >
> > > > > [...]
> > > > >
> > > > > > + * 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_regs {
> > > > > > + 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 pwm_cfg;
> > > > > > + unsigned int pwm_offt_low;
> > > > > > + unsigned int pwm_ont_low;
> > > > > > + unsigned int gen_cfg;
> > > > > > + unsigned int ext_cfg;
> > > > > > +};
> > > > > > +
> > > > > > +struct adp5585_info {
> > > > > > + const struct mfd_cell *adp5585_devs;
> > > > >
> > > > > Okay, we are never doing this. Either use OF for platform registration
> > > > > or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
> > > >
> > > > When I upstreamed the initial driver, I modelled the different functions
> > > > through child nodes in DT, with a compatible string for each child. I
> > > > was told very strongly to remove that. We have therefore no other choice
> > > > than constructing the name of the cells based on the model of the main
> > > > device.
> > >
> > > It's okay to add this information statically in this driver. It's not
> > > okay to then pass it through the OF API. You can pass an identifier
> > > through the .data attribute to match on, but we are not passing MFD cell
> > > data through like this.
> >
> > Sorry, I'm not following you. What's the issue with the .data field
> > pointing to an instance of a structure that lists properties related to
> > the device model ?
>
> There isn't one. By all means place any type of platform data you want
> in there. Similar to the information you'd find in Device Tree or the
> old board-files type pdata. You can even extract the platform data you
> pass through the OF API and place it into MFD platform data. The line
> is being drawn on passing through one type of initialisation API with
> another, MFD through OF in this case. MFD cells containing device
> registration data (including platform data!) is not itself platform
> data.
I'm still not following you. The issue will likely go away in the next
version anyway, as the MFD cells registration code needs to be rewritten
to be more dynamic.
> > > > > > + const struct regmap_config *regmap_config;
> > > > > > + const struct adp5585_regs *regs;
> > > > > > + unsigned int n_devs;
> > > > > > + unsigned int id;
> > > > >
> > > > > What ID is this? We already have platform IDs and MFD cell IDs.
> > > >
> > > > That's the value of the hardware model ID read-only register, it is used
> > > > as a safety check to verify that the connected device corresponds to the
> > > > compatible string.
> > >
> > > I suggest changing the nomenclature to be more forthcoming.
> > >
> > > 'model', 'version', 'hwid', 'chipid', etc.
> > >
> > > Why is it being stored? Is it used to match on at a later date?
> >
> > The adp5585_info structure contains static information the describe each
> > device model. There's one global static const instance per device model,
> > and they are referenced from device id structures (e.g. of_device_id).
> > The driver gets an info pointer corresponding to the model reported by
> > the platform firmware (e.g. DT). It reads the device ID from the device
> > at probe time, and compares it with the value stored in the structure as
> > a safety check to ensure there's no mismatch.
>
> I think the current implementation (as a whole, not just the IDs) needs
> a rethink. Very few attributes are changing here, both between the 2
> platforms and the several variants you're trying to support, leading to
> masses of repetition.
>
> Looking at the static configuration here, this is starting to look like
> 2 pieces of hardware with the only variation within each being the
> default register values. Is that a correct assumption?
The variants of the ADP5585 differ mainly by how they handle the default
configuration of pull-up and pull-down resistors. The consequence on the
driver side is limited to default register values, yes.
ADP5589 differs more significantly from the ADP5585. Differences between
the ADP5589 variants are small as far as I understand (datasheets are
public, should you want to have a look).
> If so, does
> mean all of this added complexity is just to configure a few register
> values such that the two platforms can be used for different things? Or
> are these really 6 true hardware variants of one another?
They are different physical chips with different product numbers.
> Either way, this approach doesn't scale. Instead of multiplying the
> amount of platforms / variants together and creating that number of
> static structs, I'd suggest using templating and only adapting what
> actually changes.
>
> For instance, the following attributes in 'struct regmap_config' never
> change; reg_bits, val_bits, and cache_type. And max_register only
> changes between the 2 hardware platforms. The reg_defaults_raw values
> can be changed in a switch statement.
All the fields of the adp5585_info structure that you would like to
dynamically set would then need to be stored in the adp5585 structure.
The would essentially trade static const data for dynamic data and more
code. Is that a personal coding style preference, or are there clear
advantages ?
> Same goes for 'struct adp5585_info'. Only regmap_config changes between
> variants. Everything else is exactly the same.
I assume this comment relates only to the different variants of the info
structure for the same model (e.g. ADP5585 or ADP5589). There are more
differences between the ADP5585 and ADP5589 entries.
> So, with the use of a
> few of templates and a couple of succinct switch cases, you can control
> all of the differentiation you need. And for every variant you wish to
> add, it's a couple of extra lines rather than many, leading to a
> much more scaleable implementation.
That also seems like a personal coding style preference :-) Adding a new
compatible variant with the existing approach only requires adding an
instance of the info structure, while your proposal would require
changes in multiple places. It seems more work to me (from a personal
preference point of view).
Of course, if the new variant requires developing abstractions that
don't exist (such as supporting large differences in the registers
layout as needed for the ADP5589), refactoring of the code will always
be required. This seems a bit of a theoretical concern though, as I'm
not aware of any other chip that would require such development.
In any case, let's see how the next version will look like, after
reworking the MFD cells registration code. Maybe it will make everybody
happy :-)
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-25 9:13 ` Laurent Pinchart
@ 2025-04-25 9:23 ` Lee Jones
2025-04-28 9:24 ` Nuno Sá
1 sibling, 0 replies; 81+ messages in thread
From: Lee Jones @ 2025-04-25 9:23 UTC (permalink / raw)
To: Laurent Pinchart
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 Fri, 25 Apr 2025, Laurent Pinchart wrote:
> Hi Lee,
>
> On Fri, Apr 25, 2025 at 08:58:59AM +0100, Lee Jones wrote:
> > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > On Thu, Apr 24, 2025 at 05:38:30PM +0100, Lee Jones wrote:
> > > > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > > > On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > > > > > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> > > > > >
> > > > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > > > >
> > > > > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder,
> > > > > > > programmable logic, reset generator, and PWM generator.
> > > > > > >
> > > > > > > This patch adds the foundation to add support for the adp5589 gpio and pwm
> > > > > > > drivers. Most importantly, we need to differentiate between some
> > > > > > > registers addresses. It also hints to future keymap support.
> > > > > > >
> > > > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > > > ---
> > > > > > > drivers/mfd/adp5585.c | 223 +++++++++++++++++++++++++++++++++++++++++---
> > > > > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > > > >
> > > > > > [...]
> > > > > >
> > > > > > > + * 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_regs {
> > > > > > > + 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 pwm_cfg;
> > > > > > > + unsigned int pwm_offt_low;
> > > > > > > + unsigned int pwm_ont_low;
> > > > > > > + unsigned int gen_cfg;
> > > > > > > + unsigned int ext_cfg;
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct adp5585_info {
> > > > > > > + const struct mfd_cell *adp5585_devs;
> > > > > >
> > > > > > Okay, we are never doing this. Either use OF for platform registration
> > > > > > or use MFD (or ACPI or PCI), but please do not pass MFD data through OF.
> > > > >
> > > > > When I upstreamed the initial driver, I modelled the different functions
> > > > > through child nodes in DT, with a compatible string for each child. I
> > > > > was told very strongly to remove that. We have therefore no other choice
> > > > > than constructing the name of the cells based on the model of the main
> > > > > device.
> > > >
> > > > It's okay to add this information statically in this driver. It's not
> > > > okay to then pass it through the OF API. You can pass an identifier
> > > > through the .data attribute to match on, but we are not passing MFD cell
> > > > data through like this.
> > >
> > > Sorry, I'm not following you. What's the issue with the .data field
> > > pointing to an instance of a structure that lists properties related to
> > > the device model ?
> >
> > There isn't one. By all means place any type of platform data you want
> > in there. Similar to the information you'd find in Device Tree or the
> > old board-files type pdata. You can even extract the platform data you
> > pass through the OF API and place it into MFD platform data. The line
> > is being drawn on passing through one type of initialisation API with
> > another, MFD through OF in this case. MFD cells containing device
> > registration data (including platform data!) is not itself platform
> > data.
>
> I'm still not following you. The issue will likely go away in the next
> version anyway, as the MFD cells registration code needs to be rewritten
> to be more dynamic.
>
> > > > > > > + const struct regmap_config *regmap_config;
> > > > > > > + const struct adp5585_regs *regs;
> > > > > > > + unsigned int n_devs;
> > > > > > > + unsigned int id;
> > > > > >
> > > > > > What ID is this? We already have platform IDs and MFD cell IDs.
> > > > >
> > > > > That's the value of the hardware model ID read-only register, it is used
> > > > > as a safety check to verify that the connected device corresponds to the
> > > > > compatible string.
> > > >
> > > > I suggest changing the nomenclature to be more forthcoming.
> > > >
> > > > 'model', 'version', 'hwid', 'chipid', etc.
> > > >
> > > > Why is it being stored? Is it used to match on at a later date?
> > >
> > > The adp5585_info structure contains static information the describe each
> > > device model. There's one global static const instance per device model,
> > > and they are referenced from device id structures (e.g. of_device_id).
> > > The driver gets an info pointer corresponding to the model reported by
> > > the platform firmware (e.g. DT). It reads the device ID from the device
> > > at probe time, and compares it with the value stored in the structure as
> > > a safety check to ensure there's no mismatch.
> >
> > I think the current implementation (as a whole, not just the IDs) needs
> > a rethink. Very few attributes are changing here, both between the 2
> > platforms and the several variants you're trying to support, leading to
> > masses of repetition.
> >
> > Looking at the static configuration here, this is starting to look like
> > 2 pieces of hardware with the only variation within each being the
> > default register values. Is that a correct assumption?
>
> The variants of the ADP5585 differ mainly by how they handle the default
> configuration of pull-up and pull-down resistors. The consequence on the
> driver side is limited to default register values, yes.
>
> ADP5589 differs more significantly from the ADP5585. Differences between
> the ADP5589 variants are small as far as I understand (datasheets are
> public, should you want to have a look).
>
> > If so, does
> > mean all of this added complexity is just to configure a few register
> > values such that the two platforms can be used for different things? Or
> > are these really 6 true hardware variants of one another?
>
> They are different physical chips with different product numbers.
>
> > Either way, this approach doesn't scale. Instead of multiplying the
> > amount of platforms / variants together and creating that number of
> > static structs, I'd suggest using templating and only adapting what
> > actually changes.
> >
> > For instance, the following attributes in 'struct regmap_config' never
> > change; reg_bits, val_bits, and cache_type. And max_register only
> > changes between the 2 hardware platforms. The reg_defaults_raw values
> > can be changed in a switch statement.
>
> All the fields of the adp5585_info structure that you would like to
> dynamically set would then need to be stored in the adp5585 structure.
> The would essentially trade static const data for dynamic data and more
> code. Is that a personal coding style preference, or are there clear
> advantages ?
>
> > Same goes for 'struct adp5585_info'. Only regmap_config changes between
> > variants. Everything else is exactly the same.
>
> I assume this comment relates only to the different variants of the info
> structure for the same model (e.g. ADP5585 or ADP5589). There are more
> differences between the ADP5585 and ADP5589 entries.
>
> > So, with the use of a
> > few of templates and a couple of succinct switch cases, you can control
> > all of the differentiation you need. And for every variant you wish to
> > add, it's a couple of extra lines rather than many, leading to a
> > much more scaleable implementation.
>
> That also seems like a personal coding style preference :-) Adding a new
> compatible variant with the existing approach only requires adding an
> instance of the info structure, while your proposal would require
> changes in multiple places. It seems more work to me (from a personal
> preference point of view).
>
> Of course, if the new variant requires developing abstractions that
> don't exist (such as supporting large differences in the registers
> layout as needed for the ADP5589), refactoring of the code will always
> be required. This seems a bit of a theoretical concern though, as I'm
> not aware of any other chip that would require such development.
>
> In any case, let's see how the next version will look like, after
> reworking the MFD cells registration code. Maybe it will make everybody
> happy :-)
Let's hope. =:)
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 06/17] mfd: adp5585: add support for adp5589
2025-04-25 9:13 ` Laurent Pinchart
2025-04-25 9:23 ` Lee Jones
@ 2025-04-28 9:24 ` Nuno Sá
1 sibling, 0 replies; 81+ messages in thread
From: Nuno Sá @ 2025-04-28 9:24 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 Fri, 2025-04-25 at 12:13 +0300, Laurent Pinchart wrote:
> Hi Lee,
>
> On Fri, Apr 25, 2025 at 08:58:59AM +0100, Lee Jones wrote:
> > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > On Thu, Apr 24, 2025 at 05:38:30PM +0100, Lee Jones wrote:
> > > > On Thu, 24 Apr 2025, Laurent Pinchart wrote:
> > > > > On Thu, Apr 24, 2025 at 05:18:38PM +0100, Lee Jones wrote:
> > > > > > On Tue, 15 Apr 2025, Nuno Sá via B4 Relay wrote:
> > > > > >
> > > > > > > From: Nuno Sá <nuno.sa@analog.com>
> > > > > > >
> > > > > > > The ADP5589 is a 19 I/O port expander with built-in keypad matrix
> > > > > > > decoder,
> > > > > > > programmable logic, reset generator, and PWM generator.
> > > > > > >
> > > > > > > This patch adds the foundation to add support for the adp5589 gpio
> > > > > > > and pwm
> > > > > > > drivers. Most importantly, we need to differentiate between some
> > > > > > > registers addresses. It also hints to future keymap support.
> > > > > > >
> > > > > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > > > > > ---
> > > > > > > drivers/mfd/adp5585.c | 223
> > > > > > > +++++++++++++++++++++++++++++++++++++++++---
> > > > > > > include/linux/mfd/adp5585.h | 57 ++++++++++-
> > > > > > > 2 files changed, 268 insertions(+), 12 deletions(-)
> > > > > >
> > > > > > [...]
> > > > > >
> > > > > > > + * 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_regs {
> > > > > > > + 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 pwm_cfg;
> > > > > > > + unsigned int pwm_offt_low;
> > > > > > > + unsigned int pwm_ont_low;
> > > > > > > + unsigned int gen_cfg;
> > > > > > > + unsigned int ext_cfg;
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct adp5585_info {
> > > > > > > + const struct mfd_cell *adp5585_devs;
> > > > > >
> > > > > > Okay, we are never doing this. Either use OF for platform
> > > > > > registration
> > > > > > or use MFD (or ACPI or PCI), but please do not pass MFD data through
> > > > > > OF.
> > > > >
> > > > > When I upstreamed the initial driver, I modelled the different
> > > > > functions
> > > > > through child nodes in DT, with a compatible string for each child. I
> > > > > was told very strongly to remove that. We have therefore no other
> > > > > choice
> > > > > than constructing the name of the cells based on the model of the main
> > > > > device.
> > > >
> > > > It's okay to add this information statically in this driver. It's not
> > > > okay to then pass it through the OF API. You can pass an identifier
> > > > through the .data attribute to match on, but we are not passing MFD cell
> > > > data through like this.
> > >
> > > Sorry, I'm not following you. What's the issue with the .data field
> > > pointing to an instance of a structure that lists properties related to
> > > the device model ?
> >
> > There isn't one. By all means place any type of platform data you want
> > in there. Similar to the information you'd find in Device Tree or the
> > old board-files type pdata. You can even extract the platform data you
> > pass through the OF API and place it into MFD platform data. The line
> > is being drawn on passing through one type of initialisation API with
> > another, MFD through OF in this case. MFD cells containing device
> > registration data (including platform data!) is not itself platform
> > data.
>
> I'm still not following you. The issue will likely go away in the next
> version anyway, as the MFD cells registration code needs to be rewritten
> to be more dynamic.
Not sure if there's any real issue but I think Lee's main concern is passing MFD
related data (struct mfd_cell) though OF (via the of table). Not sure if this is
one of the things Lee does not like but in theory, like this, you can get this
data from child platform devices for example.
>
> > > > > > > + const struct regmap_config *regmap_config;
> > > > > > > + const struct adp5585_regs *regs;
> > > > > > > + unsigned int n_devs;
> > > > > > > + unsigned int id;
> > > > > >
> > > > > > What ID is this? We already have platform IDs and MFD cell IDs.
> > > > >
> > > > > That's the value of the hardware model ID read-only register, it is
> > > > > used
> > > > > as a safety check to verify that the connected device corresponds to
> > > > > the
> > > > > compatible string.
> > > >
> > > > I suggest changing the nomenclature to be more forthcoming.
> > > >
> > > > 'model', 'version', 'hwid', 'chipid', etc.
> > > >
> > > > Why is it being stored? Is it used to match on at a later date?
> > >
> > > The adp5585_info structure contains static information the describe each
> > > device model. There's one global static const instance per device model,
> > > and they are referenced from device id structures (e.g. of_device_id).
> > > The driver gets an info pointer corresponding to the model reported by
> > > the platform firmware (e.g. DT). It reads the device ID from the device
> > > at probe time, and compares it with the value stored in the structure as
> > > a safety check to ensure there's no mismatch.
> >
> > I think the current implementation (as a whole, not just the IDs) needs
> > a rethink. Very few attributes are changing here, both between the 2
> > platforms and the several variants you're trying to support, leading to
> > masses of repetition.
> >
> > Looking at the static configuration here, this is starting to look like
> > 2 pieces of hardware with the only variation within each being the
> > default register values. Is that a correct assumption?
>
> The variants of the ADP5585 differ mainly by how they handle the default
> configuration of pull-up and pull-down resistors. The consequence on the
> driver side is limited to default register values, yes.
>
> ADP5589 differs more significantly from the ADP5585. Differences between
> the ADP5589 variants are small as far as I understand (datasheets are
> public, should you want to have a look).
>
> > If so, does
> > mean all of this added complexity is just to configure a few register
> > values such that the two platforms can be used for different things? Or
> > are these really 6 true hardware variants of one another?
>
> They are different physical chips with different product numbers.
>
> > Either way, this approach doesn't scale. Instead of multiplying the
> > amount of platforms / variants together and creating that number of
> > static structs, I'd suggest using templating and only adapting what
> > actually changes.
> >
> > For instance, the following attributes in 'struct regmap_config' never
> > change; reg_bits, val_bits, and cache_type. And max_register only
> > changes between the 2 hardware platforms. The reg_defaults_raw values
> > can be changed in a switch statement.
>
> All the fields of the adp5585_info structure that you would like to
> dynamically set would then need to be stored in the adp5585 structure.
> The would essentially trade static const data for dynamic data and more
> code. Is that a personal coding style preference, or are there clear
> advantages ?
>
> > Same goes for 'struct adp5585_info'. Only regmap_config changes between
> > variants. Everything else is exactly the same.
>
> I assume this comment relates only to the different variants of the info
> structure for the same model (e.g. ADP5585 or ADP5589). There are more
> differences between the ADP5585 and ADP5589 entries.
>
> > So, with the use of a
> > few of templates and a couple of succinct switch cases, you can control
> > all of the differentiation you need. And for every variant you wish to
> > add, it's a couple of extra lines rather than many, leading to a
> > much more scaleable implementation.
>
> That also seems like a personal coding style preference :-) Adding a new
> compatible variant with the existing approach only requires adding an
> instance of the info structure, while your proposal would require
> changes in multiple places. It seems more work to me (from a personal
> preference point of view).
>
> Of course, if the new variant requires developing abstractions that
> don't exist (such as supporting large differences in the registers
> layout as needed for the ADP5589), refactoring of the code will always
> be required. This seems a bit of a theoretical concern though, as I'm
> not aware of any other chip that would require such development.
>
> In any case, let's see how the next version will look like, after
> reworking the MFD cells registration code. Maybe it will make everybody
> happy :-)
Let's see! There's a lot to cover for v3 :sweat_smile:.I think I got the general
idea that Lee proposed. I'll probably try it so we can have a taste of it in v3.
If needed we can then rollback (not ideal, but that's life).
- Nuno Sá
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events
2025-04-15 14:49 ` [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
2025-04-21 9:28 ` Laurent Pinchart
@ 2025-04-29 15:03 ` Rob Herring (Arm)
1 sibling, 0 replies; 81+ messages in thread
From: Rob Herring (Arm) @ 2025-04-29 15:03 UTC (permalink / raw)
To: Nuno Sá
Cc: devicetree, linux-input, Krzysztof Kozlowski, Liu Ying,
Uwe Kleine-König, linux-pwm, linux-gpio, Laurent Pinchart,
Conor Dooley, Bartosz Golaszewski, Linus Walleij, Lee Jones,
Dmitry Torokhov
On Tue, 15 Apr 2025 15:49:25 +0100, Nuno Sá wrote:
> 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.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
> .../devicetree/bindings/mfd/adi,adp5585.yaml | 188 ++++++++++++++++++++-
> 1 file changed, 186 insertions(+), 2 deletions(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
` (18 preceding siblings ...)
2025-04-16 9:02 ` Liu Ying
@ 2025-05-01 12:00 ` Lee Jones
2025-05-01 14:09 ` Laurent Pinchart
19 siblings, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-05-01 12:00 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, Krzysztof Kozlowski
On Tue, 15 Apr 2025 15:49:16 +0100, Nuno Sá wrote:
> The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> and PWM. However, the gpio part of it was already supported as part of
> the keyboard driver:
>
> https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
>
> On top of that it also overlapped with my refactoring of the above driver [1]
> to drop usage of platform data and use FW properties instead.
>
> [...]
Applied, thanks!
[01/17] dt-bindings: mfd: adp5585: ease on the required properties
commit: 3a2ea3e9f369bdae939bcccff67a77a6281dca74
[02/17] mfd: adp5585: enable oscilator during probe
commit: 7353f196fd73b79e30ff750d93caf096ed660e1b
[03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
commit: 7c7e9f08a1a9bf16b6c1942c2e0cb919da855970
[04/17] mfd: adp5585: make use of MFD_CELL_NAME()
commit: e72e9148d017535b39500d0aad624d0a0fcd2ce7
[05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
commit: 6da01b9d833c5efbce7c2e30dde276e0d29105f8
[06/17] mfd: adp5585: add support for adp5589
commit: 382dc0327b8a9ee03c901df9b85134c68917becc
[07/17] gpio: adp5585: add support for the ad5589 expander
commit: cff3cef09595001140bd29aedf33fc84998bf77c
[08/17] pwm: adp5585: add support for adp5589
commit: 333b66fd3edfe18db4dc16041328a89144b73067
[09/17] dt-bindings: mfd: adp5585: add properties for input events
commit: 7bdb41d7a85e1c6244da57d4dcc491df962ff3fb
[10/17] mfd: adp5585: add support for key events
commit: 8814ac45c75fcce55896bc376a97b56f392925c3
[11/17] gpio: adp5585: support gpi events
commit: 8f3d9b44c5c5ada312d0ef71ec0181011854a95b
[12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
commit: a53fc67a1e21a8507821263946b1d65687b0284f
[13/17] Input: adp5589: remove the driver
commit: 216c99cf1002a42f896b54fab09823e8ba46b218
[14/17] mfd: adp5585: support getting vdd regulator
commit: 63a8717f744d51ea0c8228e09db4233d48f2f9ba
[15/17] dt-bindings: mfd: adp5585: document reset gpio
commit: 49c887f0547bc14eb50ba20e1c8acb7255af3b86
[16/17] mfd: adp5585: add support for a reset pin
commit: 01c328823459456fb99469cc37f270f70d41fd2a
[17/17] pwm: adp5585: make sure to include mod_devicetable.h
(no commit info)
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-05-01 12:00 ` Lee Jones
@ 2025-05-01 14:09 ` Laurent Pinchart
2025-05-02 7:13 ` Lee Jones
0 siblings, 1 reply; 81+ messages in thread
From: Laurent Pinchart @ 2025-05-01 14:09 UTC (permalink / raw)
To: Lee Jones
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Nuno Sá,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Liu Ying, Krzysztof Kozlowski
Hi Lee,
On Thu, May 01, 2025 at 01:00:03PM +0100, Lee Jones wrote:
> On Tue, 15 Apr 2025 15:49:16 +0100, Nuno Sá wrote:
> > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > and PWM. However, the gpio part of it was already supported as part of
> > the keyboard driver:
> >
> > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> >
> > On top of that it also overlapped with my refactoring of the above driver [1]
> > to drop usage of platform data and use FW properties instead.
> >
> > [...]
>
> Applied, thanks!
>
> [01/17] dt-bindings: mfd: adp5585: ease on the required properties
> commit: 3a2ea3e9f369bdae939bcccff67a77a6281dca74
> [02/17] mfd: adp5585: enable oscilator during probe
> commit: 7353f196fd73b79e30ff750d93caf096ed660e1b
> [03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
> commit: 7c7e9f08a1a9bf16b6c1942c2e0cb919da855970
> [04/17] mfd: adp5585: make use of MFD_CELL_NAME()
> commit: e72e9148d017535b39500d0aad624d0a0fcd2ce7
> [05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
> commit: 6da01b9d833c5efbce7c2e30dde276e0d29105f8
> [06/17] mfd: adp5585: add support for adp5589
> commit: 382dc0327b8a9ee03c901df9b85134c68917becc
> [07/17] gpio: adp5585: add support for the ad5589 expander
> commit: cff3cef09595001140bd29aedf33fc84998bf77c
> [08/17] pwm: adp5585: add support for adp5589
> commit: 333b66fd3edfe18db4dc16041328a89144b73067
> [09/17] dt-bindings: mfd: adp5585: add properties for input events
> commit: 7bdb41d7a85e1c6244da57d4dcc491df962ff3fb
> [10/17] mfd: adp5585: add support for key events
> commit: 8814ac45c75fcce55896bc376a97b56f392925c3
> [11/17] gpio: adp5585: support gpi events
> commit: 8f3d9b44c5c5ada312d0ef71ec0181011854a95b
> [12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
> commit: a53fc67a1e21a8507821263946b1d65687b0284f
> [13/17] Input: adp5589: remove the driver
> commit: 216c99cf1002a42f896b54fab09823e8ba46b218
> [14/17] mfd: adp5585: support getting vdd regulator
> commit: 63a8717f744d51ea0c8228e09db4233d48f2f9ba
> [15/17] dt-bindings: mfd: adp5585: document reset gpio
> commit: 49c887f0547bc14eb50ba20e1c8acb7255af3b86
> [16/17] mfd: adp5585: add support for a reset pin
> commit: 01c328823459456fb99469cc37f270f70d41fd2a
> [17/17] pwm: adp5585: make sure to include mod_devicetable.h
> (no commit info)
I'm a bit surprised, didn't you ask for changes, calling for a v3 ?
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-05-01 14:09 ` Laurent Pinchart
@ 2025-05-02 7:13 ` Lee Jones
2025-05-02 7:35 ` Nuno Sá
0 siblings, 1 reply; 81+ messages in thread
From: Lee Jones @ 2025-05-02 7:13 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Nuno Sá,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Liu Ying, Krzysztof Kozlowski
On Thu, 01 May 2025, Laurent Pinchart wrote:
> Hi Lee,
>
> On Thu, May 01, 2025 at 01:00:03PM +0100, Lee Jones wrote:
> > On Tue, 15 Apr 2025 15:49:16 +0100, Nuno Sá wrote:
> > > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > > and PWM. However, the gpio part of it was already supported as part of
> > > the keyboard driver:
> > >
> > > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> > >
> > > On top of that it also overlapped with my refactoring of the above driver [1]
> > > to drop usage of platform data and use FW properties instead.
> > >
> > > [...]
> >
> > Applied, thanks!
> >
> > [01/17] dt-bindings: mfd: adp5585: ease on the required properties
> > commit: 3a2ea3e9f369bdae939bcccff67a77a6281dca74
> > [02/17] mfd: adp5585: enable oscilator during probe
> > commit: 7353f196fd73b79e30ff750d93caf096ed660e1b
> > [03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
> > commit: 7c7e9f08a1a9bf16b6c1942c2e0cb919da855970
> > [04/17] mfd: adp5585: make use of MFD_CELL_NAME()
> > commit: e72e9148d017535b39500d0aad624d0a0fcd2ce7
> > [05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
> > commit: 6da01b9d833c5efbce7c2e30dde276e0d29105f8
> > [06/17] mfd: adp5585: add support for adp5589
> > commit: 382dc0327b8a9ee03c901df9b85134c68917becc
> > [07/17] gpio: adp5585: add support for the ad5589 expander
> > commit: cff3cef09595001140bd29aedf33fc84998bf77c
> > [08/17] pwm: adp5585: add support for adp5589
> > commit: 333b66fd3edfe18db4dc16041328a89144b73067
> > [09/17] dt-bindings: mfd: adp5585: add properties for input events
> > commit: 7bdb41d7a85e1c6244da57d4dcc491df962ff3fb
> > [10/17] mfd: adp5585: add support for key events
> > commit: 8814ac45c75fcce55896bc376a97b56f392925c3
> > [11/17] gpio: adp5585: support gpi events
> > commit: 8f3d9b44c5c5ada312d0ef71ec0181011854a95b
> > [12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
> > commit: a53fc67a1e21a8507821263946b1d65687b0284f
> > [13/17] Input: adp5589: remove the driver
> > commit: 216c99cf1002a42f896b54fab09823e8ba46b218
> > [14/17] mfd: adp5585: support getting vdd regulator
> > commit: 63a8717f744d51ea0c8228e09db4233d48f2f9ba
> > [15/17] dt-bindings: mfd: adp5585: document reset gpio
> > commit: 49c887f0547bc14eb50ba20e1c8acb7255af3b86
> > [16/17] mfd: adp5585: add support for a reset pin
> > commit: 01c328823459456fb99469cc37f270f70d41fd2a
> > [17/17] pwm: adp5585: make sure to include mod_devicetable.h
> > (no commit info)
>
> I'm a bit surprised, didn't you ask for changes, calling for a v3 ?
Yes, sorry. My fault. Tooling error. Please disregard.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-05-02 7:13 ` Lee Jones
@ 2025-05-02 7:35 ` Nuno Sá
2025-05-02 8:30 ` Lee Jones
0 siblings, 1 reply; 81+ messages in thread
From: Nuno Sá @ 2025-05-02 7:35 UTC (permalink / raw)
To: Lee Jones, Laurent Pinchart
Cc: linux-gpio, linux-pwm, devicetree, linux-input, Nuno Sá,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Liu Ying, Krzysztof Kozlowski
On Fri, 2025-05-02 at 08:13 +0100, Lee Jones wrote:
> On Thu, 01 May 2025, Laurent Pinchart wrote:
>
> > Hi Lee,
> >
> > On Thu, May 01, 2025 at 01:00:03PM +0100, Lee Jones wrote:
> > > On Tue, 15 Apr 2025 15:49:16 +0100, Nuno Sá wrote:
> > > > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > > > and PWM. However, the gpio part of it was already supported as part of
> > > > the keyboard driver:
> > > >
> > > > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> > > >
> > > > On top of that it also overlapped with my refactoring of the above
> > > > driver [1]
> > > > to drop usage of platform data and use FW properties instead.
> > > >
> > > > [...]
> > >
> > > Applied, thanks!
> > >
> > > [01/17] dt-bindings: mfd: adp5585: ease on the required properties
> > > commit: 3a2ea3e9f369bdae939bcccff67a77a6281dca74
> > > [02/17] mfd: adp5585: enable oscilator during probe
> > > commit: 7353f196fd73b79e30ff750d93caf096ed660e1b
> > > [03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
> > > commit: 7c7e9f08a1a9bf16b6c1942c2e0cb919da855970
> > > [04/17] mfd: adp5585: make use of MFD_CELL_NAME()
> > > commit: e72e9148d017535b39500d0aad624d0a0fcd2ce7
> > > [05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
> > > commit: 6da01b9d833c5efbce7c2e30dde276e0d29105f8
> > > [06/17] mfd: adp5585: add support for adp5589
> > > commit: 382dc0327b8a9ee03c901df9b85134c68917becc
> > > [07/17] gpio: adp5585: add support for the ad5589 expander
> > > commit: cff3cef09595001140bd29aedf33fc84998bf77c
> > > [08/17] pwm: adp5585: add support for adp5589
> > > commit: 333b66fd3edfe18db4dc16041328a89144b73067
> > > [09/17] dt-bindings: mfd: adp5585: add properties for input events
> > > commit: 7bdb41d7a85e1c6244da57d4dcc491df962ff3fb
> > > [10/17] mfd: adp5585: add support for key events
> > > commit: 8814ac45c75fcce55896bc376a97b56f392925c3
> > > [11/17] gpio: adp5585: support gpi events
> > > commit: 8f3d9b44c5c5ada312d0ef71ec0181011854a95b
> > > [12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
> > > commit: a53fc67a1e21a8507821263946b1d65687b0284f
> > > [13/17] Input: adp5589: remove the driver
> > > commit: 216c99cf1002a42f896b54fab09823e8ba46b218
> > > [14/17] mfd: adp5585: support getting vdd regulator
> > > commit: 63a8717f744d51ea0c8228e09db4233d48f2f9ba
> > > [15/17] dt-bindings: mfd: adp5585: document reset gpio
> > > commit: 49c887f0547bc14eb50ba20e1c8acb7255af3b86
> > > [16/17] mfd: adp5585: add support for a reset pin
> > > commit: 01c328823459456fb99469cc37f270f70d41fd2a
> > > [17/17] pwm: adp5585: make sure to include mod_devicetable.h
> > > (no commit info)
> >
> > I'm a bit surprised, didn't you ask for changes, calling for a v3 ?
>
> Yes, sorry. My fault. Tooling error. Please disregard.
I was wondering the same...
- Nuno Sá
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver
2025-05-02 7:35 ` Nuno Sá
@ 2025-05-02 8:30 ` Lee Jones
0 siblings, 0 replies; 81+ messages in thread
From: Lee Jones @ 2025-05-02 8:30 UTC (permalink / raw)
To: Nuno Sá
Cc: Laurent Pinchart, linux-gpio, linux-pwm, devicetree, linux-input,
Nuno Sá, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Liu Ying, Krzysztof Kozlowski
On Fri, 02 May 2025, Nuno Sá wrote:
> On Fri, 2025-05-02 at 08:13 +0100, Lee Jones wrote:
> > On Thu, 01 May 2025, Laurent Pinchart wrote:
> >
> > > Hi Lee,
> > >
> > > On Thu, May 01, 2025 at 01:00:03PM +0100, Lee Jones wrote:
> > > > On Tue, 15 Apr 2025 15:49:16 +0100, Nuno Sá wrote:
> > > > > The adp5585 MFD driver was introduced in 6.11 adding support for gpio
> > > > > and PWM. However, the gpio part of it was already supported as part of
> > > > > the keyboard driver:
> > > > >
> > > > > https://elixir.bootlin.com/linux/v6.14-rc6/source/drivers/input/keyboard/adp5589-keys.c#L532
> > > > >
> > > > > On top of that it also overlapped with my refactoring of the above
> > > > > driver [1]
> > > > > to drop usage of platform data and use FW properties instead.
> > > > >
> > > > > [...]
> > > >
> > > > Applied, thanks!
> > > >
> > > > [01/17] dt-bindings: mfd: adp5585: ease on the required properties
> > > > commit: 3a2ea3e9f369bdae939bcccff67a77a6281dca74
> > > > [02/17] mfd: adp5585: enable oscilator during probe
> > > > commit: 7353f196fd73b79e30ff750d93caf096ed660e1b
> > > > [03/17] pwm: adp5585: don't control OSC_EN in the pwm driver
> > > > commit: 7c7e9f08a1a9bf16b6c1942c2e0cb919da855970
> > > > [04/17] mfd: adp5585: make use of MFD_CELL_NAME()
> > > > commit: e72e9148d017535b39500d0aad624d0a0fcd2ce7
> > > > [05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander
> > > > commit: 6da01b9d833c5efbce7c2e30dde276e0d29105f8
> > > > [06/17] mfd: adp5585: add support for adp5589
> > > > commit: 382dc0327b8a9ee03c901df9b85134c68917becc
> > > > [07/17] gpio: adp5585: add support for the ad5589 expander
> > > > commit: cff3cef09595001140bd29aedf33fc84998bf77c
> > > > [08/17] pwm: adp5585: add support for adp5589
> > > > commit: 333b66fd3edfe18db4dc16041328a89144b73067
> > > > [09/17] dt-bindings: mfd: adp5585: add properties for input events
> > > > commit: 7bdb41d7a85e1c6244da57d4dcc491df962ff3fb
> > > > [10/17] mfd: adp5585: add support for key events
> > > > commit: 8814ac45c75fcce55896bc376a97b56f392925c3
> > > > [11/17] gpio: adp5585: support gpi events
> > > > commit: 8f3d9b44c5c5ada312d0ef71ec0181011854a95b
> > > > [12/17] Input: adp5585: Add Analog Devices ADP5585/89 support
> > > > commit: a53fc67a1e21a8507821263946b1d65687b0284f
> > > > [13/17] Input: adp5589: remove the driver
> > > > commit: 216c99cf1002a42f896b54fab09823e8ba46b218
> > > > [14/17] mfd: adp5585: support getting vdd regulator
> > > > commit: 63a8717f744d51ea0c8228e09db4233d48f2f9ba
> > > > [15/17] dt-bindings: mfd: adp5585: document reset gpio
> > > > commit: 49c887f0547bc14eb50ba20e1c8acb7255af3b86
> > > > [16/17] mfd: adp5585: add support for a reset pin
> > > > commit: 01c328823459456fb99469cc37f270f70d41fd2a
> > > > [17/17] pwm: adp5585: make sure to include mod_devicetable.h
> > > > (no commit info)
> > >
> > > I'm a bit surprised, didn't you ask for changes, calling for a v3 ?
> >
> > Yes, sorry. My fault. Tooling error. Please disregard.
>
> I was wondering the same...
Found out what happened.
I applied them all to grep for the spelling issue reported by Colin.
However, I hit return (sending the thank-yous) instead of Ctrl+c (to
cancel them).
Apologies for the confusion.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 81+ messages in thread
end of thread, other threads:[~2025-05-02 8:31 UTC | newest]
Thread overview: 81+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-15 14:49 [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 01/17] dt-bindings: mfd: adp5585: ease on the required properties Nuno Sá via B4 Relay
2025-04-21 8:56 ` Laurent Pinchart
2025-04-21 12:12 ` Nuno Sá
2025-04-21 12:29 ` Laurent Pinchart
2025-04-21 12:45 ` Nuno Sá
2025-04-21 18:57 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 02/17] mfd: adp5585: enable oscilator during probe Nuno Sá via B4 Relay
2025-04-21 8:57 ` Laurent Pinchart
2025-04-21 12:14 ` Nuno Sá
2025-04-21 22:03 ` Laurent Pinchart
2025-04-22 7:50 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 03/17] pwm: adp5585: don't control OSC_EN in the pwm driver Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 04/17] mfd: adp5585: make use of MFD_CELL_NAME() Nuno Sá via B4 Relay
2025-04-21 9:03 ` Laurent Pinchart
2025-04-24 15:55 ` Lee Jones
2025-04-15 14:49 ` [PATCH v2 05/17] dt-bindings: mfd: adp5585: document adp5589 I/O expander Nuno Sá via B4 Relay
2025-04-21 9:07 ` Laurent Pinchart
2025-04-21 18:59 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 06/17] mfd: adp5585: add support for adp5589 Nuno Sá via B4 Relay
2025-04-21 9:15 ` Laurent Pinchart
2025-04-21 12:21 ` Nuno Sá
2025-04-21 22:06 ` Laurent Pinchart
2025-04-22 7:51 ` Nuno Sá
2025-04-24 16:01 ` Lee Jones
2025-04-24 16:18 ` Lee Jones
2025-04-24 16:30 ` Laurent Pinchart
2025-04-24 16:38 ` Lee Jones
2025-04-24 19:39 ` Laurent Pinchart
2025-04-25 7:58 ` Lee Jones
2025-04-25 9:13 ` Laurent Pinchart
2025-04-25 9:23 ` Lee Jones
2025-04-28 9:24 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 07/17] gpio: adp5585: add support for the ad5589 expander Nuno Sá via B4 Relay
2025-04-17 12:27 ` Bartosz Golaszewski
2025-04-21 9:23 ` Laurent Pinchart
2025-04-21 12:22 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 08/17] pwm: adp5585: add support for adp5589 Nuno Sá via B4 Relay
2025-04-15 14:49 ` [PATCH v2 09/17] dt-bindings: mfd: adp5585: add properties for input events Nuno Sá via B4 Relay
2025-04-21 9:28 ` Laurent Pinchart
2025-04-21 12:24 ` Nuno Sá
2025-04-29 15:03 ` Rob Herring (Arm)
2025-04-15 14:49 ` [PATCH v2 10/17] mfd: adp5585: add support for key events Nuno Sá via B4 Relay
2025-04-21 9:33 ` Laurent Pinchart
2025-04-21 12:32 ` Nuno Sá
2025-04-21 22:13 ` Laurent Pinchart
2025-04-24 16:07 ` Lee Jones
2025-04-24 16:24 ` Laurent Pinchart
2025-04-24 16:28 ` Lee Jones
2025-04-15 14:49 ` [PATCH v2 11/17] gpio: adp5585: support gpi events Nuno Sá via B4 Relay
2025-04-17 12:28 ` Bartosz Golaszewski
2025-04-15 14:49 ` [PATCH v2 12/17] Input: adp5585: Add Analog Devices ADP5585/89 support Nuno Sá via B4 Relay
2025-04-19 2:44 ` Dmitry Torokhov
2025-04-21 9:35 ` Laurent Pinchart
2025-04-21 12:33 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 13/17] Input: adp5589: remove the driver Nuno Sá via B4 Relay
2025-04-19 2:45 ` Dmitry Torokhov
2025-04-21 9:40 ` Laurent Pinchart
2025-04-21 12:34 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 14/17] mfd: adp5585: support getting vdd regulator Nuno Sá via B4 Relay
2025-04-21 9:48 ` Laurent Pinchart
2025-04-21 12:38 ` Nuno Sá
2025-04-21 22:09 ` Laurent Pinchart
2025-04-22 8:12 ` Nuno Sá
2025-04-15 14:49 ` [PATCH v2 15/17] dt-bindings: mfd: adp5585: document reset gpio Nuno Sá via B4 Relay
2025-04-21 9:36 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 16/17] mfd: adp5585: add support for a reset pin Nuno Sá via B4 Relay
2025-04-21 9:46 ` Laurent Pinchart
2025-04-21 12:42 ` Nuno Sá
2025-04-21 22:10 ` Laurent Pinchart
2025-04-15 14:49 ` [PATCH v2 17/17] pwm: adp5585: make sure to include mod_devicetable.h Nuno Sá via B4 Relay
2025-04-21 9:50 ` Laurent Pinchart
2025-04-15 15:56 ` [PATCH v2 00/17] mfd: adp5585: support keymap events and drop legacy Input driver Laurent Pinchart
2025-04-21 10:08 ` Laurent Pinchart
2025-04-16 9:02 ` Liu Ying
2025-04-16 10:03 ` Nuno Sá
2025-05-01 12:00 ` Lee Jones
2025-05-01 14:09 ` Laurent Pinchart
2025-05-02 7:13 ` Lee Jones
2025-05-02 7:35 ` Nuno Sá
2025-05-02 8:30 ` 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).