* [PATCH v2 0/7] Add support for MAX7360
@ 2024-12-23 16:42 Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
` (7 more replies)
0 siblings, 8 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
This series implements a set of drivers allowing to support the Maxim
Integrated MAX7360 device.
The MAX7360 is an I2C key-switch and led controller, with following
functionalities:
- Keypad controller for a key matrix of up to 8 rows and 8 columns.
- Rotary encoder support, for a single rotary encoder.
- Up to 8 PWM outputs.
- Up to 8 GPIOs with support for interrupts and 6 GPOs.
Chipset pins are shared between all functionalities, so all cannot be
used at the same time.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
Changes in v2:
- Removing device tree subnodes for keypad, rotary encoder and pwm
functionalities.
- Fixed dt-bindings syntax and naming.
- Fixed missing handling of requested period in PWM driver.
- Cleanup of the code
- Link to v1: https://lore.kernel.org/r/20241219-mdb-max7360-support-v1-0-8e8317584121@bootlin.com
---
Kamel Bouhara (2):
mfd: Add max7360 support
pwm: max7360: Add MAX7360 PWM support
Mathieu Dubois-Briand (5):
dt-bindings: mfd: gpio: Add MAX7360
gpio: max7360: Add MAX7360 gpio support
input: keyboard: Add support for MAX7360 keypad
input: misc: Add support for MAX7360 rotary
MAINTAINERS: Add entry on MAX7360 driver
.../bindings/gpio/maxim,max7360-gpio.yaml | 80 ++++
.../devicetree/bindings/mfd/maxim,max7360.yaml | 107 +++++
MAINTAINERS | 12 +
drivers/gpio/Kconfig | 11 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max7360.c | 455 +++++++++++++++++++++
drivers/input/keyboard/Kconfig | 12 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/max7360-keypad.c | 289 +++++++++++++
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/max7360-rotary.c | 185 +++++++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max7360.c | 296 ++++++++++++++
drivers/pwm/Kconfig | 11 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-max7360.c | 219 ++++++++++
include/linux/mfd/max7360.h | 109 +++++
19 files changed, 1814 insertions(+)
---
base-commit: 78d4f34e2115b517bcbfe7ec0d018bbbb6f9b0b8
change-id: 20241219-mdb-max7360-support-223a8ce45ba3
Best regards,
--
Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
@ 2024-12-23 16:42 ` Mathieu Dubois-Briand
2024-12-23 17:08 ` Uwe Kleine-König
2024-12-24 9:12 ` Krzysztof Kozlowski
2024-12-23 16:42 ` [PATCH v2 2/7] mfd: Add max7360 support mathieu.dubois-briand
` (6 subsequent siblings)
7 siblings, 2 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
Add device tree bindings for Maxim Integrated MAX7360 device with
support for keypad, rotary, gpios and pwm functionalities.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
.../bindings/gpio/maxim,max7360-gpio.yaml | 80 +++++++++++++++
.../devicetree/bindings/mfd/maxim,max7360.yaml | 107 +++++++++++++++++++++
2 files changed, 187 insertions(+)
diff --git a/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
new file mode 100644
index 000000000000..6e6133ce6e68
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim MAX7360 GPIO controller
+
+maintainers:
+ - Kamel Bouhara <kamel.bouhara@bootlin.com>
+ - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+
+description: |
+ Maxim MAX7360 GPIO controller, in MAX7360 chipset
+ https://www.analog.com/en/products/max7360.html
+
+properties:
+ compatible:
+ enum:
+ - maxim,max7360-gpio
+ - maxim,max7360-gpo
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
+ ngpios:
+ minimum: 0
+ maximum: 8
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
+ maxim,constant-current-disable:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Bit field, each bit disables constant-current output of the associated
+ GPIO, starting from the least significant bit for the first GPIO.
+
+
+required:
+ - compatible
+ - gpio-controller
+ - ngpios
+
+if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - maxim,max7360-gpio
+then:
+ required:
+ - interrupt-controller
+else:
+ properties:
+ interrupt-controller: false
+ maxim,constant-current-disable: false
+
+ ngpios:
+ maximum: 6
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio {
+ compatible = "maxim,max7360-gpio";
+
+ gpio-controller;
+ #gpio-cells = <0x2>;
+ ngpios = <8>;
+ maxim,constant-current-disable = <0x06>;
+
+ interrupt-controller;
+ #interrupt-cells = <0x2>;
+ };
diff --git a/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
new file mode 100644
index 000000000000..1f761707070a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller
+
+maintainers:
+ - Kamel Bouhara <kamel.bouhara@bootlin.com>
+ - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+
+description: |
+ Maxim MAX7360 device, with following functions:
+ - keypad controller
+ - rotary controller
+ - GPIO and GPO controller
+ - PWM controller
+
+ https://www.analog.com/en/products/max7360.html
+
+allOf:
+ - $ref: /schemas/input/matrix-keymap.yaml#
+ - $ref: /schemas/input/input.yaml#
+
+properties:
+ compatible:
+ enum:
+ - maxim,max7360
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 2
+ maxItems: 2
+
+ interrupt-names:
+ items:
+ - const: inti
+ - const: intk
+
+ keypad-debounce-delay-ms:
+ description: Keypad debounce delay in ms
+ minimum: 9
+ maximum: 40
+ default: 9
+
+ autorepeat: true
+
+ rotary-debounce-delay-ms:
+ description: Rotary encoder debounce delay in ms
+ minimum: 0
+ maximum: 15
+ default: 0
+
+ linux,axis:
+ description: The input subsystem axis to map to this rotary encoder.
+
+ "#pwm-cells":
+ const: 3
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - linux,keymap
+ - linux,axis
+ - "#pwm-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/input/input.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ io-expander@38 {
+ compatible = "maxim,max7360";
+ reg = <0x38>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <23 IRQ_TYPE_LEVEL_LOW>,
+ <24 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "inti", "intk";
+
+ keypad,num-rows = <8>;
+ keypad,num-columns = <4>;
+ linux,keymap = <
+ MATRIX_KEY(0x00, 0x00, KEY_F5)
+ MATRIX_KEY(0x01, 0x00, KEY_F4)
+ MATRIX_KEY(0x02, 0x01, KEY_F6)
+ >;
+ keypad-debounce-delay-ms = <10>;
+ autorepeat;
+
+ rotary-debounce-delay-ms = <2>;
+ linux,axis = <0>; /* REL_X */
+
+ #pwm-cells = <3>;
+ };
+ };
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 2/7] mfd: Add max7360 support
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
@ 2024-12-23 16:42 ` mathieu.dubois-briand
2024-12-23 16:42 ` [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support mathieu.dubois-briand
` (5 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: mathieu.dubois-briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
From: Kamel Bouhara <kamel.bouhara@bootlin.com>
Add core driver to support MAX7360 i2c chip, multi function device
with keypad, gpio, pwm, gpo and rotary encoder submodules.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Co-developed-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/mfd/Kconfig | 12 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/max7360.c | 296 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/max7360.h | 109 ++++++++++++++++
4 files changed, 418 insertions(+)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ae23b317a64e..c1ddda480922 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2414,5 +2414,17 @@ config MFD_RSMU_SPI
Additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_MAX7360
+ tristate "Maxim MAX7360 Support"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for Maxim MAX7360.
+ This driver provides common support for accessing
+ the device; additional drivers must be enabled in
+ order to use the functionality of the device.
+
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e057d6d6faef..6cd55504106d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -163,6 +163,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o
+obj-$(CONFIG_MFD_MAX7360) += max7360.o
obj-$(CONFIG_MFD_MAX77541) += max77541.o
obj-$(CONFIG_MFD_MAX77620) += max77620.o
obj-$(CONFIG_MFD_MAX77650) += max77650.o
diff --git a/drivers/mfd/max7360.c b/drivers/mfd/max7360.c
new file mode 100644
index 000000000000..e2751b4f68b2
--- /dev/null
+++ b/drivers/mfd/max7360.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim MAX7360 Core Driver
+ *
+ * Copyright (C) 2024 Kamel Bouhara
+ * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max7360.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+static DEFINE_SPINLOCK(request_lock);
+
+struct max7360_mfd {
+ struct regmap *regmap;
+ unsigned int requested_ports;
+ struct device *dev;
+};
+
+#define GPO_COMPATIBLE "maxim,max7360-gpo"
+#define GPIO_COMPATIBLE "maxim,max7360-gpio"
+
+static const struct mfd_cell max7360_cells[] = {
+ {
+ .name = MAX7360_DRVNAME_PWM,
+ },
+ {
+ .name = MAX7360_DRVNAME_GPO,
+ .of_compatible = GPO_COMPATIBLE,
+ },
+ {
+ .name = MAX7360_DRVNAME_GPIO,
+ .of_compatible = GPIO_COMPATIBLE,
+ },
+ {
+ .name = MAX7360_DRVNAME_KEYPAD,
+ },
+ {
+ .name = MAX7360_DRVNAME_ROTARY,
+ },
+};
+
+static const struct regmap_range max7360_volatile_ranges[] = {
+ {
+ .range_min = MAX7360_REG_KEYFIFO,
+ .range_max = MAX7360_REG_KEYFIFO,
+ }, {
+ .range_min = 0x48,
+ .range_max = 0x4a,
+ },
+};
+
+static const struct regmap_access_table max7360_volatile_table = {
+ .yes_ranges = max7360_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges),
+};
+
+static const struct regmap_config max7360_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .volatile_table = &max7360_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max7360_set_gpos_count(struct max7360_mfd *max7360_mfd)
+{
+ /*
+ * Max7360 COL0 to COL7 pins can be used either as keypad columns,
+ * general purpose output or a mix of both.
+ * Get the number of pins requested by the corresponding drivers, ensure
+ * they are compatible with each others and apply the corresponding
+ * configuration.
+ */
+ struct device_node *np;
+ u32 gpos = 0;
+ u32 columns = 0;
+ unsigned int val;
+ int ret;
+
+ np = of_get_compatible_child(max7360_mfd->dev->of_node, GPO_COMPATIBLE);
+ if (np) {
+ ret = of_property_read_u32(np, "ngpios", &gpos);
+ if (ret < 0) {
+ dev_err(max7360_mfd->dev, "Failed to read gpos count\n");
+ return ret;
+ }
+ }
+
+ ret = device_property_read_u32(max7360_mfd->dev,
+ "keypad,num-columns", &columns);
+ if (ret < 0) {
+ dev_err(max7360_mfd->dev, "Failed to read columns count\n");
+ return ret;
+ }
+
+ if (gpos > MAX7360_MAX_GPO ||
+ (gpos + columns > MAX7360_MAX_KEY_COLS)) {
+ dev_err(max7360_mfd->dev,
+ "Incompatible gpos and columns count (%u, %u)\n",
+ gpos, columns);
+ return -EINVAL;
+ }
+
+ /*
+ * MAX7360_REG_DEBOUNCE contains configuration both for keypad debounce
+ * timings and gpos/keypad columns repartition. Only the later is
+ * modified here.
+ */
+ val = FIELD_PREP(MAX7360_PORTS, gpos);
+ ret = regmap_write_bits(max7360_mfd->regmap, MAX7360_REG_DEBOUNCE,
+ MAX7360_PORTS, val);
+ if (ret) {
+ dev_err(max7360_mfd->dev,
+ "Failed to write max7360 columns/gpos configuration");
+ return ret;
+ }
+
+ return 0;
+}
+
+int max7360_port_pin_request(struct device *dev, unsigned int pin, bool request)
+{
+ struct i2c_client *client;
+ struct max7360_mfd *max7360_mfd;
+ unsigned long flags;
+ int ret = 0;
+
+ client = to_i2c_client(dev);
+ max7360_mfd = i2c_get_clientdata(client);
+
+ spin_lock_irqsave(&request_lock, flags);
+ if (request) {
+ if (max7360_mfd->requested_ports & BIT(pin))
+ ret = -EBUSY;
+ else
+ max7360_mfd->requested_ports |= BIT(pin);
+ } else {
+ max7360_mfd->requested_ports &= ~BIT(pin);
+ }
+ spin_unlock_irqrestore(&request_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max7360_port_pin_request);
+
+static int max7360_mask_irqs(struct max7360_mfd *max7360_mfd)
+{
+ unsigned int i;
+ unsigned int val;
+ int ret;
+
+ /*
+ * GPIO/PWM interrupts are not masked on reset: mask the during probe,
+ * avoiding repeated spurious interrupts if the corresponding drivers
+ * are not present.
+ */
+ for (i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {
+ ret = regmap_write_bits(max7360_mfd->regmap,
+ MAX7360_REG_PWMCFG + i,
+ MAX7360_PORT_CFG_INTERRUPT_MASK,
+ MAX7360_PORT_CFG_INTERRUPT_MASK);
+ if (ret) {
+ dev_err(max7360_mfd->dev,
+ "failed to write max7360 port configuration");
+ return ret;
+ }
+ }
+
+ /* Read gpio in register, to ack any pending IRQ.
+ */
+ ret = regmap_read(max7360_mfd->regmap, MAX7360_REG_GPIOIN, &val);
+ if (ret) {
+ dev_err(max7360_mfd->dev, "Failed to read gpio values: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max7360_reset(struct max7360_mfd *max7360_mfd)
+{
+ int err;
+
+ /*
+ * Set back the default values.
+ * We do not use GPIO reset function here, as it does not work reliably.
+ */
+ err = regmap_write(max7360_mfd->regmap, MAX7360_REG_GPIODEB, 0x00);
+ if (err) {
+ dev_err(max7360_mfd->dev, "Failed to set configuration\n");
+ return err;
+ }
+
+ err = regmap_write(max7360_mfd->regmap, MAX7360_REG_GPIOCURR, MAX7360_REG_GPIOCURR_FIXED);
+ if (err) {
+ dev_err(max7360_mfd->dev, "Failed to set configuration\n");
+ return err;
+ }
+
+ err = regmap_write(max7360_mfd->regmap, MAX7360_REG_GPIOOUTM, 0x00);
+ if (err) {
+ dev_err(max7360_mfd->dev, "Failed to set configuration\n");
+ return err;
+ }
+
+ err = regmap_write(max7360_mfd->regmap, MAX7360_REG_PWMCOM, 0x00);
+ if (err) {
+ dev_err(max7360_mfd->dev, "Failed to set configuration\n");
+ return err;
+ }
+
+ err = regmap_write(max7360_mfd->regmap, MAX7360_REG_SLEEP, 0);
+ if (err) {
+ dev_err(max7360_mfd->dev, "Failed to set configuration\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int max7360_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ struct max7360_mfd *max7360_mfd;
+ int err;
+
+ regmap = devm_regmap_init_i2c(client, &max7360_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to initialise regmap\n");
+
+ max7360_mfd = devm_kzalloc(dev, sizeof(*max7360_mfd), GFP_KERNEL);
+ if (!max7360_mfd)
+ return -ENOMEM;
+
+ max7360_mfd->regmap = regmap;
+ max7360_mfd->dev = dev;
+ i2c_set_clientdata(client, max7360_mfd);
+
+ err = max7360_reset(max7360_mfd);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to reset device\n");
+
+ err = max7360_set_gpos_count(max7360_mfd);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to set GPOS pin count\n");
+
+ /*
+ * Get the device out of shutdown mode.
+ */
+ err = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,
+ MAX7360_GPIO_CFG_GPIO_EN,
+ MAX7360_GPIO_CFG_GPIO_EN);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to set device out of shutdown\n");
+
+ err = max7360_mask_irqs(max7360_mfd);
+ if (err)
+ return dev_err_probe(dev, err, "could not mask interrupts\n");
+
+ err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ max7360_cells, ARRAY_SIZE(max7360_cells),
+ NULL, 0, NULL);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register child devices\n");
+
+ return 0;
+}
+
+static const struct of_device_id max7360_dt_match[] = {
+ { .compatible = "maxim,max7360" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max7360_dt_match);
+
+static struct i2c_driver max7360_driver = {
+ .driver = {
+ .name = "max7360",
+ .of_match_table = max7360_dt_match,
+ },
+ .probe = max7360_probe,
+};
+module_i2c_driver(max7360_driver);
+
+MODULE_DESCRIPTION("Maxim MAX7360 MFD core driver");
+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max7360.h b/include/linux/mfd/max7360.h
new file mode 100644
index 000000000000..2665a8e6b0f0
--- /dev/null
+++ b/include/linux/mfd/max7360.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __LINUX_MFD_MAX7360_H
+#define __LINUX_MFD_MAX7360_H
+#include <linux/bitfield.h>
+#include <linux/device.h>
+
+#define MAX7360_MAX_KEY_ROWS 8
+#define MAX7360_MAX_KEY_COLS 8
+#define MAX7360_MAX_KEY_NUM (MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS)
+#define MAX7360_ROW_SHIFT 3
+
+#define MAX7360_MAX_GPIO 8
+#define MAX7360_MAX_GPO 6
+#define MAX7360_PORT_PWM_COUNT 8
+#define MAX7360_PORT_RTR_PIN (MAX7360_PORT_PWM_COUNT - 1)
+/*
+ * MAX7360 registers
+ */
+#define MAX7360_REG_KEYFIFO 0x00
+#define MAX7360_REG_CONFIG 0x01
+#define MAX7360_REG_DEBOUNCE 0x02
+#define MAX7360_REG_INTERRUPT 0x03
+#define MAX7360_REG_PORTS 0x04
+#define MAX7360_REG_KEYREP 0x05
+#define MAX7360_REG_SLEEP 0x06
+
+/*
+ * MAX7360 registers
+ */
+#define MAX7360_REG_GPIOCFG 0x40
+#define MAX7360_REG_GPIOCTRL 0x41
+#define MAX7360_REG_GPIODEB 0x42
+#define MAX7360_REG_GPIOCURR 0x43
+#define MAX7360_REG_GPIOOUTM 0x44
+#define MAX7360_REG_PWMCOM 0x45
+#define MAX7360_REG_RTRCFG 0x46
+#define MAX7360_REG_GPIOIN 0x49
+#define MAX7360_REG_RTR_CNT 0x4A
+#define MAX7360_REG_PWMBASE 0x50
+#define MAX7360_REG_PWMCFG 0x58
+
+#define MAX7360_REG_PORTCFGBASE 0x58
+
+/*
+ * Configuration register bits
+ */
+#define MAX7360_FIFO_EMPTY 0x3f
+#define MAX7360_FIFO_OVERFLOW 0x7f
+#define MAX7360_FIFO_RELEASE BIT(6)
+#define MAX7360_FIFO_COL GENMASK(5, 3)
+#define MAX7360_FIFO_ROW GENMASK(2, 0)
+
+#define MAX7360_CFG_SLEEP BIT(7)
+#define MAX7360_CFG_INTERRUPT BIT(5)
+#define MAX7360_CFG_KEY_RELEASE BIT(3)
+#define MAX7360_CFG_WAKEUP BIT(1)
+#define MAX7360_CFG_TIMEOUT BIT(0)
+
+#define MAX7360_DEBOUNCE GENMASK(4, 0)
+#define MAX7360_DEBOUNCE_MIN 9
+#define MAX7360_DEBOUNCE_MAX 40
+#define MAX7360_PORTS GENMASK(8, 5)
+
+#define MAX7360_INTERRUPT_TIME_MASK GENMASK(4, 0)
+#define MAX7360_INTERRUPT_FIFO_MASK GENMASK(7, 5)
+
+#define MAX7360_PORT_CFG_INTERRUPT_MASK BIT(7)
+#define MAX7360_PORT_CFG_INTERRUPT_EDGES BIT(6)
+
+#define MAX7360_REG_GPIOCURR_FIXED 0xC0
+
+/*
+ * Autosleep register values (ms)
+ */
+#define MAX7360_AUTOSLEEP_8192 0x01
+#define MAX7360_AUTOSLEEP_4096 0x02
+#define MAX7360_AUTOSLEEP_2048 0x03
+#define MAX7360_AUTOSLEEP_1024 0x04
+#define MAX7360_AUTOSLEEP_512 0x05
+#define MAX7360_AUTOSLEEP_256 0x06
+
+#define MAX7360_GPIO_CFG_RTR_EN BIT(7)
+#define MAX7360_GPIO_CFG_GPIO_EN BIT(4)
+#define MAX7360_GPIO_CFG_GPIO_RST BIT(3)
+
+#define MAX7360_ROT_DEBOUNCE GENMASK(3, 0)
+#define MAX7360_ROT_DEBOUNCE_MIN 0
+#define MAX7360_ROT_DEBOUNCE_MAX 15
+#define MAX7360_ROT_INTCNT GENMASK(6, 4)
+#define MAX7360_ROT_INTCNT_DLY BIT(7)
+
+#define MAX7360_INT_INTI 0
+#define MAX7360_INT_INTK 1
+
+#define MAX7360_INT_GPIO 0
+#define MAX7360_INT_KEYPAD 1
+#define MAX7360_INT_ROTARY 2
+
+#define MAX7360_NR_INTERNAL_IRQS 3
+
+#define MAX7360_DRVNAME_PWM "max7360-pwm"
+#define MAX7360_DRVNAME_GPO "max7360-gpo"
+#define MAX7360_DRVNAME_GPIO "max7360-gpio"
+#define MAX7360_DRVNAME_KEYPAD "max7360-keypad"
+#define MAX7360_DRVNAME_ROTARY "max7360-rotary"
+
+int max7360_port_pin_request(struct device *dev, unsigned int pin, bool request);
+
+#endif
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 2/7] mfd: Add max7360 support mathieu.dubois-briand
@ 2024-12-23 16:42 ` mathieu.dubois-briand
2024-12-31 17:31 ` Christophe JAILLET
2024-12-23 16:42 ` [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support Mathieu Dubois-Briand
` (4 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: mathieu.dubois-briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
From: Kamel Bouhara <kamel.bouhara@bootlin.com>
Add driver for Maxim Integrated MAX7360 PWM controller, supporting up to
8 independent PWM outputs.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/pwm/Kconfig | 11 +++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-max7360.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 231 insertions(+)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0915c1e7df16..399dc3f76e92 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -745,4 +745,15 @@ config PWM_XILINX
To compile this driver as a module, choose M here: the module
will be called pwm-xilinx.
+config PWM_MAX7360
+ tristate "MAX7360 PWMs"
+ depends on MFD_MAX7360
+ depends on OF_GPIO
+ help
+ PWM driver for Maxim Integrated MAX7360 multifunction device, with
+ support for up to 8 PWM outputs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-max7360.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 9081e0c0e9e0..ae8908ffc892 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o
obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o
+obj-$(CONFIG_PWM_MAX7360) += pwm-max7360.o
obj-$(CONFIG_PWM_MESON) += pwm-meson.o
obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o
obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o
diff --git a/drivers/pwm/pwm-max7360.c b/drivers/pwm/pwm-max7360.c
new file mode 100644
index 000000000000..28de6c6140e1
--- /dev/null
+++ b/drivers/pwm/pwm-max7360.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Bootlin
+ *
+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>
+ *
+ * Limitations:
+ * - Only supports normal polarity.
+ * - The period is fixed to 2 ms.
+ * - Only the duty cycle can be changed, new values are applied at the beginning
+ * of the next cycle.
+ * - When disabled, the output is put in Hi-Z.
+ */
+#include <linux/math.h>
+#include <linux/mfd/max7360.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define MAX7360_NUM_PWMS 8
+#define MAX7360_PWM_MAX_RES 256
+#define MAX7360_PWM_PERIOD_NS 2000000 /* 500 Hz */
+#define MAX7360_PWM_COMMON_PWN BIT(5)
+#define MAX7360_PWM_CTRL_ENABLE(n) BIT(n)
+#define MAX7360_PWM_PORT(n) BIT(n)
+
+struct max7360_pwm {
+ struct device *parent;
+ struct regmap *regmap;
+};
+
+static inline struct max7360_pwm *to_max7360_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct max7360_pwm *max7360_pwm;
+ int ret;
+
+ max7360_pwm = to_max7360_pwm(chip);
+ ret = max7360_port_pin_request(max7360_pwm->parent, pwm->hwpwm,
+ true);
+ if (ret) {
+ dev_warn(&chip->dev, "failed to request pwm-%d\n", pwm->hwpwm);
+ return ret;
+ }
+
+ ret = regmap_write_bits(max7360_pwm->regmap,
+ MAX7360_REG_PWMCFG + pwm->hwpwm,
+ MAX7360_PWM_COMMON_PWN,
+ 0);
+ if (ret) {
+ dev_warn(&chip->dev,
+ "failed to write pwm-%d cfg register, error %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+
+ ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_PORTS,
+ MAX7360_PWM_PORT(pwm->hwpwm),
+ MAX7360_PWM_PORT(pwm->hwpwm));
+ if (ret) {
+ dev_warn(&chip->dev,
+ "failed to write pwm-%d ports register, error %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void max7360_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct max7360_pwm *max7360_pwm;
+ int ret;
+
+ max7360_pwm = to_max7360_pwm(chip);
+ ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL,
+ MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm),
+ 0);
+ if (ret)
+ dev_warn(&chip->dev, "failed to disable pwm-%d , error %d\n",
+ pwm->hwpwm, ret);
+
+ max7360_port_pin_request(max7360_pwm->parent, pwm->hwpwm,
+ false);
+}
+
+static int max7360_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct max7360_pwm *max7360_pwm;
+ u64 duty_steps;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->period != MAX7360_PWM_PERIOD_NS) {
+ dev_warn(&chip->dev,
+ "unsupported pwm period: %llu, should be %u\n",
+ state->period, MAX7360_PWM_PERIOD_NS);
+ return -EINVAL;
+ }
+
+ duty_steps = mul_u64_u64_div_u64(state->duty_cycle, MAX7360_PWM_MAX_RES,
+ MAX7360_PWM_PERIOD_NS);
+
+ max7360_pwm = to_max7360_pwm(chip);
+ ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL,
+ MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm),
+ MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm));
+ if (ret) {
+ dev_warn(&chip->dev, "failed to enable pwm-%d , error %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+
+ ret = regmap_write(max7360_pwm->regmap, MAX7360_REG_PWMBASE + pwm->hwpwm,
+ duty_steps >= 255 ? 255 : duty_steps);
+ if (ret) {
+ dev_warn(&chip->dev,
+ "failed to apply pwm duty_cycle %llu on pwm-%d, error %d\n",
+ duty_steps, pwm->hwpwm, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max7360_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct max7360_pwm *max7360_pwm;
+ unsigned int val;
+ int ret;
+
+ max7360_pwm = to_max7360_pwm(chip);
+
+ state->period = MAX7360_PWM_PERIOD_NS;
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ ret = regmap_read(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL, &val);
+ if (ret) {
+ dev_warn(&chip->dev,
+ "failed to read pwm configuration on pwm-%d, error %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+ state->enabled = !!(val & MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm));
+
+ ret = regmap_read(max7360_pwm->regmap, MAX7360_REG_PWMBASE + pwm->hwpwm,
+ &val);
+ if (ret) {
+ dev_warn(&chip->dev,
+ "failed to read pwm duty_cycle on pwm-%d, error %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+ state->duty_cycle = mul_u64_u64_div_u64(val, MAX7360_PWM_PERIOD_NS,
+ MAX7360_PWM_MAX_RES);
+
+ return 0;
+}
+
+static const struct pwm_ops max7360_pwm_ops = {
+ .request = max7360_pwm_request,
+ .free = max7360_pwm_free,
+ .apply = max7360_pwm_apply,
+ .get_state = max7360_pwm_get_state,
+};
+
+static int max7360_pwm_probe(struct platform_device *pdev)
+{
+ struct max7360_pwm *max7360_pwm;
+ struct pwm_chip *chip;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return dev_err_probe(&pdev->dev, -ENODEV, "no parent device\n");
+
+ chip = devm_pwmchip_alloc(pdev->dev.parent, MAX7360_NUM_PWMS,
+ sizeof(*max7360_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ chip->ops = &max7360_pwm_ops;
+
+ max7360_pwm = to_max7360_pwm(chip);
+ max7360_pwm->parent = pdev->dev.parent;
+
+ max7360_pwm->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!max7360_pwm->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "could not get parent regmap\n");
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret != 0)
+ dev_err_probe(&pdev->dev, ret, "failed to add PWM chip");
+
+ return 0;
+}
+
+static struct platform_driver max7360_pwm_driver = {
+ .driver = {
+ .name = MAX7360_DRVNAME_PWM,
+ },
+ .probe = max7360_pwm_probe,
+};
+module_platform_driver(max7360_pwm_driver);
+
+MODULE_DESCRIPTION("MAX7360 PWM driver");
+MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");
+MODULE_ALIAS("platform:" MAX7360_DRVNAME_PWM);
+MODULE_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
` (2 preceding siblings ...)
2024-12-23 16:42 ` [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support mathieu.dubois-briand
@ 2024-12-23 16:42 ` Mathieu Dubois-Briand
2024-12-31 17:40 ` Christophe JAILLET
2025-01-21 15:20 ` Andy Shevchenko
2024-12-23 16:42 ` [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad Mathieu Dubois-Briand
` (3 subsequent siblings)
7 siblings, 2 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
Add driver for Maxim Integrated MAX7360 GPIO/GPO controller.
Two sets of GPIOs are provided by the device:
- Up to 8 GPIOs, shared with the PWM and rotary encoder functionalities.
These GPIOs also provide interrupts on input changes.
- Up to 6 GPOs, on unused keypad columns pins.
Co-developed-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/gpio/Kconfig | 11 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max7360.c | 455 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 467 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93ee3aa092f8..efe07e21c442 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1444,6 +1444,17 @@ config GPIO_MADERA
help
Support for GPIOs on Cirrus Logic Madera class codecs.
+config GPIO_MAX7360
+ tristate "MAX7360 GPIO support"
+ depends on MFD_MAX7360
+ depends on OF_GPIO
+ help
+ Allows to use MAX7360 I/O Expander PWM lines as GPIO and keypad COL
+ lines as GPO.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-max7360.
+
config GPIO_MAX77620
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index af3ba4d81b58..581341b3e3e4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
+obj-$(CONFIG_GPIO_MAX7360) += gpio-max7360.o
obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
diff --git a/drivers/gpio/gpio-max7360.c b/drivers/gpio/gpio-max7360.c
new file mode 100644
index 000000000000..42f7f3e66d3a
--- /dev/null
+++ b/drivers/gpio/gpio-max7360.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Bootlin
+ *
+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>
+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max7360.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define MAX7360_GPIO_PORT 1
+#define MAX7360_GPIO_COL 2
+
+struct max7360_gpio {
+ struct gpio_chip chip;
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned long gpio_function;
+
+ /*
+ * Interrupts handling data: only used when gpio_function is
+ * MAX7360_GPIO_PORT.
+ */
+ u8 masked_interrupts;
+ u8 in_values;
+ unsigned int irq_types[MAX7360_MAX_GPIO];
+};
+
+static void max7360_gpio_set_value(struct gpio_chip *gc,
+ unsigned int pin, int state)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+ int ret;
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL) {
+ int off = MAX7360_MAX_GPIO - (gc->ngpio - pin);
+
+ ret = regmap_write_bits(max7360_gpio->regmap, MAX7360_REG_PORTS,
+ BIT(off), state ? BIT(off) : 0);
+ } else {
+ ret = regmap_write(max7360_gpio->regmap,
+ MAX7360_REG_PWMBASE + pin, state ? 0xFF : 0);
+ }
+
+ if (ret)
+ dev_err(max7360_gpio->dev,
+ "failed to set value %d on gpio-%d", state, pin);
+}
+
+static int max7360_gpio_get_value(struct gpio_chip *gc, unsigned int pin)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+ unsigned int val;
+ int off;
+ int ret;
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL) {
+ off = MAX7360_MAX_GPIO - (gc->ngpio - pin);
+
+ ret = regmap_read(max7360_gpio->regmap, MAX7360_REG_PORTS, &val);
+ } else {
+ off = pin;
+ ret = regmap_read(max7360_gpio->regmap, MAX7360_REG_GPIOIN, &val);
+ }
+
+ if (ret) {
+ dev_err(max7360_gpio->dev, "failed to read gpio-%d", pin);
+ return ret;
+ }
+
+ return !!(val & BIT(off));
+}
+
+static int max7360_gpio_get_direction(struct gpio_chip *gc, unsigned int pin)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+ unsigned int val;
+ int ret;
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ ret = regmap_read(max7360_gpio->regmap, MAX7360_REG_GPIOCTRL, &val);
+ if (ret) {
+ dev_err(max7360_gpio->dev, "failed to read gpio-%d direction",
+ pin);
+ return ret;
+ }
+
+ if (val & BIT(pin))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int max7360_gpio_direction_input(struct gpio_chip *gc, unsigned int pin)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+ int ret;
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL)
+ return -EIO;
+
+ ret = regmap_write_bits(max7360_gpio->regmap, MAX7360_REG_GPIOCTRL,
+ BIT(pin), 0);
+ if (ret) {
+ dev_err(max7360_gpio->dev, "failed to set gpio-%d direction",
+ pin);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max7360_gpio_direction_output(struct gpio_chip *gc, unsigned int pin,
+ int state)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+ int ret;
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_PORT) {
+ ret = regmap_write_bits(max7360_gpio->regmap,
+ MAX7360_REG_GPIOCTRL, BIT(pin),
+ BIT(pin));
+ if (ret) {
+ dev_err(max7360_gpio->dev,
+ "failed to set gpio-%d direction", pin);
+ return ret;
+ }
+ }
+
+ max7360_gpio_set_value(gc, pin, state);
+
+ return 0;
+}
+
+static int max7360_gpio_request(struct gpio_chip *gc, unsigned int pin)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+
+ /*
+ * GPOs on COL pins (keypad columns) can always be requested: this
+ * driver has full access to them, up to the number set in chip.ngpio.
+ * GPIOs on PORT pins are shared with the PWM and rotary encoder
+ * drivers: they have to be requested from the MFD driver.
+ */
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL)
+ return 0;
+
+ return max7360_port_pin_request(max7360_gpio->dev->parent, pin, true);
+}
+
+static void max7360_gpio_free(struct gpio_chip *gc, unsigned int pin)
+{
+ struct max7360_gpio *max7360_gpio = gpiochip_get_data(gc);
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_COL)
+ return;
+
+ max7360_port_pin_request(max7360_gpio->dev->parent, pin, false);
+}
+
+static struct gpio_chip max7360_gpio_chip = {
+ .label = "max7360",
+ .request = max7360_gpio_request,
+ .free = max7360_gpio_free,
+ .get_direction = max7360_gpio_get_direction,
+ .direction_input = max7360_gpio_direction_input,
+ .direction_output = max7360_gpio_direction_output,
+ .get = max7360_gpio_get_value,
+ .set = max7360_gpio_set_value,
+ .base = -1,
+ .can_sleep = true,
+};
+
+static irqreturn_t max7360_gpio_irq(int irq, void *data)
+{
+ struct max7360_gpio *max7360_gpio = data;
+ unsigned long pending;
+ unsigned int gpio_irq;
+ unsigned int type;
+ unsigned int count = 0;
+ int val;
+ int irqn;
+ int ret;
+
+ ret = regmap_read(max7360_gpio->regmap, MAX7360_REG_GPIOIN, &val);
+ if (ret) {
+ dev_err(max7360_gpio->dev, "Failed to read gpio values: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /* MAX7360 generates interrupts but does not report which pins changed:
+ * compare the pin value with the value they had in previous interrupt
+ * and report interrupt if the change match the set IRQ type.
+ */
+ pending = val ^ max7360_gpio->in_values;
+ for_each_set_bit(irqn, &pending, max7360_gpio->chip.ngpio) {
+ type = max7360_gpio->irq_types[irqn];
+ if (max7360_gpio->masked_interrupts & BIT(irqn))
+ continue;
+ if ((val & BIT(irqn)) && type == IRQ_TYPE_EDGE_FALLING)
+ continue;
+ if (!(val & BIT(irqn)) && type == IRQ_TYPE_EDGE_RISING)
+ continue;
+ gpio_irq = irq_find_mapping(max7360_gpio->chip.irq.domain, irqn);
+ handle_nested_irq(gpio_irq);
+ count++;
+ }
+
+ max7360_gpio->in_values = val;
+
+ if (count == 0)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static void max7360_gpio_irq_unmask(struct irq_data *data)
+{
+ struct max7360_gpio *max7360_gpio;
+ unsigned int pin = irqd_to_hwirq(data);
+ unsigned int val;
+ int ret;
+
+ max7360_gpio = gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ /* Read current pin value, so we know if the pin changed in the next
+ * interrupt.
+ * No lock should be needed regarding the interrupt handler: as long as
+ * the corresponding bit has not been cleared in masked_interrupts, this
+ * gpio is ignored.
+ */
+ ret = regmap_read(max7360_gpio->regmap, MAX7360_REG_GPIOIN, &val);
+ if (ret)
+ dev_err(max7360_gpio->dev, "Failed to read gpio values: %d\n",
+ ret);
+
+ max7360_gpio->in_values &= ~BIT(pin);
+ max7360_gpio->in_values |= val & BIT(pin);
+
+ ret = regmap_write_bits(max7360_gpio->regmap, MAX7360_REG_PWMCFG + pin,
+ MAX7360_PORT_CFG_INTERRUPT_MASK, 0);
+
+ if (ret)
+ dev_err(max7360_gpio->dev, "failed to unmask gpio-%d", pin);
+
+ max7360_gpio->masked_interrupts &= ~BIT(pin);
+}
+
+static void max7360_gpio_irq_mask(struct irq_data *data)
+{
+ struct max7360_gpio *max7360_gpio;
+ unsigned int pin = irqd_to_hwirq(data);
+ int ret;
+
+ max7360_gpio = gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ max7360_gpio->masked_interrupts |= BIT(pin);
+
+ ret = regmap_write_bits(max7360_gpio->regmap, MAX7360_REG_PWMCFG + pin,
+ MAX7360_PORT_CFG_INTERRUPT_MASK,
+ MAX7360_PORT_CFG_INTERRUPT_MASK);
+
+ if (ret)
+ dev_err(max7360_gpio->dev, "failed to mask gpio-%d", pin);
+}
+
+static void max7360_gpio_irq_enable(struct irq_data *data)
+{
+ max7360_gpio_irq_unmask(data);
+}
+
+static void max7360_gpio_irq_disable(struct irq_data *data)
+{
+ max7360_gpio_irq_mask(data);
+}
+
+static int max7360_gpio_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
+{
+ struct max7360_gpio *max7360_gpio;
+ unsigned int pin;
+ unsigned int val;
+ int ret;
+
+ pin = irqd_to_hwirq(data);
+ max7360_gpio = gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ val = MAX7360_PORT_CFG_INTERRUPT_EDGES;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = regmap_write_bits(max7360_gpio->regmap, MAX7360_REG_PWMCFG + pin,
+ MAX7360_PORT_CFG_INTERRUPT_EDGES, val);
+
+ if (ret)
+ dev_err(max7360_gpio->dev, "failed to unmask gpio-%d", pin);
+
+ max7360_gpio->irq_types[pin] = flow_type;
+
+ return 0;
+}
+
+static const struct irq_chip max7360_gpio_irqchip = {
+ .name = "max7360",
+ .irq_enable = max7360_gpio_irq_enable,
+ .irq_disable = max7360_gpio_irq_disable,
+ .irq_mask = max7360_gpio_irq_mask,
+ .irq_unmask = max7360_gpio_irq_unmask,
+ .irq_set_type = max7360_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int max7360_gpio_probe(struct platform_device *pdev)
+{
+ struct max7360_gpio *max7360_gpio;
+ struct platform_device *parent;
+ unsigned int ngpios;
+ unsigned int outconf;
+ struct gpio_irq_chip *girq;
+ unsigned long flags;
+ int irq;
+ int ret;
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device\n");
+ return -ENODEV;
+ }
+ parent = to_platform_device(pdev->dev.parent);
+
+ max7360_gpio = devm_kzalloc(&pdev->dev, sizeof(struct max7360_gpio),
+ GFP_KERNEL);
+ if (!max7360_gpio)
+ return -ENOMEM;
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ return -ENODEV;
+ }
+
+ max7360_gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!max7360_gpio->regmap) {
+ dev_err(&pdev->dev, "could not get parent regmap\n");
+ return -ENODEV;
+ }
+
+ max7360_gpio->dev = &pdev->dev;
+ max7360_gpio->chip = max7360_gpio_chip;
+ max7360_gpio->chip.ngpio = ngpios;
+ max7360_gpio->chip.parent = &pdev->dev;
+ max7360_gpio->chip.base = -1;
+ max7360_gpio->gpio_function = (uintptr_t)device_get_match_data(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "gpio count: %d\n", max7360_gpio->chip.ngpio);
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_PORT) {
+ /* Port GPIOs: set output mode configuration (constant-current
+ * or not).
+ * This property is optional.
+ */
+ outconf = 0;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "maxim,constant-current-disable",
+ &outconf);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(&pdev->dev,
+ "Failed to read maxim,constant-current-disable OF property\n");
+ return -ENODEV;
+ }
+
+ regmap_write(max7360_gpio->regmap, MAX7360_REG_GPIOOUTM, outconf);
+ }
+
+ if (max7360_gpio->gpio_function == MAX7360_GPIO_PORT &&
+ of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) {
+ /* Port GPIOs: declare IRQ chip, if configuration was provided.
+ */
+ irq = platform_get_irq_byname(parent, "inti");
+ if (irq < 0)
+ return dev_err_probe(&pdev->dev, irq,
+ "Failed to get IRQ");
+
+ flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED;
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ max7360_gpio_irq, flags,
+ "max7360-gpio", max7360_gpio);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register interrupt: %d\n",
+ ret);
+
+ girq = &max7360_gpio->chip.irq;
+ gpio_irq_chip_set_chip(girq, &max7360_gpio_irqchip);
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->threaded = true;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ }
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &max7360_gpio->chip, max7360_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id max7360_gpio_of_match[] = {
+ {
+ .compatible = "maxim,max7360-gpo",
+ .data = (void *)MAX7360_GPIO_COL
+ }, {
+ .compatible = "maxim,max7360-gpio",
+ .data = (void *)MAX7360_GPIO_PORT
+ }, {
+ }
+};
+MODULE_DEVICE_TABLE(of, max7360_gpio_of_match);
+
+static struct platform_driver max7360_gpio_driver = {
+ .driver = {
+ .name = MAX7360_DRVNAME_GPIO,
+ .of_match_table = of_match_ptr(max7360_gpio_of_match),
+ },
+ .probe = max7360_gpio_probe,
+};
+module_platform_driver(max7360_gpio_driver);
+
+MODULE_DESCRIPTION("MAX7360 GPIO driver");
+MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");
+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
+MODULE_ALIAS("platform:" MAX7360_DRVNAME_GPIO);
+MODULE_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
` (3 preceding siblings ...)
2024-12-23 16:42 ` [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support Mathieu Dubois-Briand
@ 2024-12-23 16:42 ` Mathieu Dubois-Briand
2024-12-31 17:45 ` Christophe JAILLET
2024-12-23 16:42 ` [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary Mathieu Dubois-Briand
` (2 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
Add driver for Maxim Integrated MAX7360 keypad controller, providing
support for up to 64 keys, with a matrix of 8 columns and 8 rows.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/input/keyboard/Kconfig | 12 ++
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/max7360-keypad.c | 289 ++++++++++++++++++++++++++++++++
3 files changed, 302 insertions(+)
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 721ab69e84ac..bba029f65cfa 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -421,6 +421,18 @@ config KEYBOARD_MAX7359
To compile this driver as a module, choose M here: the
module will be called max7359_keypad.
+config KEYBOARD_MAX7360
+ tristate "Maxim MAX7360 Key Switch Controller"
+ select INPUT_MATRIXKMAP
+ depends on I2C
+ depends on MFD_MAX7360
+ help
+ If you say yes here you get support for the keypad controller on the
+ Maxim MAX7360 I/O Expander.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max7360_keypad.
+
config KEYBOARD_MPR121
tristate "Freescale MPR121 Touchkey"
depends on I2C
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1e0721c30709..b49d32d4003d 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
+obj-$(CONFIG_KEYBOARD_MAX7360) += max7360-keypad.o
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o
obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
diff --git a/drivers/input/keyboard/max7360-keypad.c b/drivers/input/keyboard/max7360-keypad.c
new file mode 100644
index 000000000000..7e5539ee1a2b
--- /dev/null
+++ b/drivers/input/keyboard/max7360-keypad.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Bootlin
+ *
+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max7360.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct max7360_keypad {
+ struct input_dev *input;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned int debounce_ms;
+ int irq;
+ struct regmap *regmap;
+ unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS];
+};
+
+static irqreturn_t max7360_keypad_irq(int irq, void *data)
+{
+ struct max7360_keypad *max7360_keypad = data;
+ unsigned int val;
+ unsigned int row, col;
+ unsigned int release;
+ unsigned int code;
+ int error;
+
+ do {
+ error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO,
+ &val);
+ if (error) {
+ dev_err(&max7360_keypad->input->dev,
+ "Failed to read max7360 FIFO");
+ return IRQ_NONE;
+ }
+
+ /* FIFO overflow: ignore it and get next event. */
+ if (val == MAX7360_FIFO_OVERFLOW)
+ dev_warn(&max7360_keypad->input->dev,
+ "max7360 FIFO overflow");
+ } while (val == MAX7360_FIFO_OVERFLOW);
+
+ if (val == MAX7360_FIFO_EMPTY) {
+ dev_dbg(&max7360_keypad->input->dev,
+ "Got a spurious interrupt");
+
+ return IRQ_NONE;
+ }
+
+ row = FIELD_GET(MAX7360_FIFO_ROW, val);
+ col = FIELD_GET(MAX7360_FIFO_COL, val);
+ release = val & MAX7360_FIFO_RELEASE;
+
+ code = MATRIX_SCAN_CODE(row, col, MAX7360_ROW_SHIFT);
+
+ dev_dbg(&max7360_keypad->input->dev,
+ "key[%d:%d] %s\n", row, col, release ? "release" : "press");
+
+ input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code);
+ input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code],
+ !release);
+ input_sync(max7360_keypad->input);
+
+ return IRQ_HANDLED;
+}
+
+static int max7360_keypad_open(struct input_dev *pdev)
+{
+ struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
+ int error;
+
+ /*
+ * Somebody is using the device: get out of sleep.
+ */
+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,
+ MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP);
+ if (error) {
+ dev_err(&max7360_keypad->input->dev,
+ "Failed to write max7360 configuration");
+ return error;
+ }
+
+ return 0;
+}
+
+static void max7360_keypad_close(struct input_dev *pdev)
+{
+ struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
+ int error;
+
+ /*
+ * Nobody is using the device anymore: go to sleep.
+ */
+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,
+ MAX7360_CFG_SLEEP, ~MAX7360_CFG_SLEEP);
+ if (error)
+ dev_err(&max7360_keypad->input->dev,
+ "Failed to write max7360 configuration");
+}
+
+static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad)
+{
+ unsigned int val;
+ int error;
+
+ val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN;
+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE,
+ MAX7360_DEBOUNCE,
+ FIELD_PREP(MAX7360_DEBOUNCE, val));
+ if (error) {
+ dev_err(&max7360_keypad->input->dev,
+ "Failed to write max7360 debounce configuration");
+ return error;
+ }
+
+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT,
+ MAX7360_INTERRUPT_TIME_MASK,
+ FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1));
+ if (error) {
+ dev_err(&max7360_keypad->input->dev,
+ "Failed to write max7360 keypad interrupt configuration");
+ return error;
+ }
+
+ return 0;
+}
+
+static int max7360_keypad_parse_dt(struct platform_device *pdev,
+ struct max7360_keypad *max7360_keypad,
+ bool *autorepeat)
+{
+ int error;
+
+ error = matrix_keypad_parse_properties(pdev->dev.parent,
+ &max7360_keypad->rows,
+ &max7360_keypad->cols);
+ if (error)
+ return error;
+
+ if (!max7360_keypad->rows || !max7360_keypad->cols ||
+ max7360_keypad->rows > MAX7360_MAX_KEY_ROWS ||
+ max7360_keypad->cols > MAX7360_MAX_KEY_COLS) {
+ dev_err(&pdev->dev,
+ "Invalid number of columns or rows (%ux%u)\n",
+ max7360_keypad->cols, max7360_keypad->rows);
+ return -EINVAL;
+ }
+
+ *autorepeat = device_property_read_bool(pdev->dev.parent, "autorepeat");
+
+ max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN;
+ error = device_property_read_u32(pdev->dev.parent,
+ "keypad-debounce-delay-ms",
+ &max7360_keypad->debounce_ms);
+ if (error == -EINVAL) {
+ dev_info(&pdev->dev, "Using default keypad-debounce-delay-ms: %u\n",
+ max7360_keypad->debounce_ms);
+ } else if (error < 0) {
+ dev_err(&pdev->dev,
+ "Failed to read keypad-debounce-delay-ms property\n");
+ return error;
+ } else if (max7360_keypad->debounce_ms < MAX7360_DEBOUNCE_MIN ||
+ max7360_keypad->debounce_ms > MAX7360_DEBOUNCE_MAX) {
+ dev_err(&pdev->dev,
+ "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n",
+ max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN,
+ MAX7360_DEBOUNCE_MAX);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max7360_keypad_probe(struct platform_device *pdev)
+{
+ struct max7360_keypad *max7360_keypad;
+ struct input_dev *input;
+ bool autorepeat;
+ int error;
+ int irq;
+
+ if (!pdev->dev.parent)
+ return dev_err_probe(&pdev->dev, -ENODEV, "No parent device\n");
+
+ irq = platform_get_irq_byname(to_platform_device(pdev->dev.parent),
+ "intk");
+ if (irq < 0)
+ return irq;
+
+ max7360_keypad = devm_kzalloc(&pdev->dev, sizeof(*max7360_keypad),
+ GFP_KERNEL);
+ if (!max7360_keypad)
+ return -ENOMEM;
+
+ max7360_keypad->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!max7360_keypad->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "Could not get parent regmap\n");
+
+ error = max7360_keypad_parse_dt(pdev, max7360_keypad, &autorepeat);
+ if (error)
+ return error;
+
+ input = devm_input_allocate_device(pdev->dev.parent);
+ if (!input)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "Failed to allocate input device\n");
+
+ max7360_keypad->input = input;
+
+ input->id.bustype = BUS_I2C;
+ input->name = pdev->name;
+ input->open = max7360_keypad_open;
+ input->close = max7360_keypad_close;
+
+ error = matrix_keypad_build_keymap(NULL, NULL,
+ MAX7360_MAX_KEY_ROWS,
+ MAX7360_MAX_KEY_COLS,
+ max7360_keypad->keycodes, input);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "Failed to build keymap\n");
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ if (autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ input_set_drvdata(input, max7360_keypad);
+
+ error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ max7360_keypad_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "max7360-keypad", max7360_keypad);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "Failed to register interrupt: %d\n",
+ error);
+
+ error = input_register_device(input);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "Could not register input device: %d\n",
+ error);
+
+ platform_set_drvdata(pdev, max7360_keypad);
+
+ error = max7360_keypad_hw_init(max7360_keypad);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "Failed to initialize max7360 keypad\n");
+
+ device_init_wakeup(&pdev->dev, true);
+ error = dev_pm_set_wake_irq(&pdev->dev, irq);
+ if (error)
+ dev_warn(&pdev->dev, "Failed to set up wakeup irq: %d\n",
+ error);
+
+ return 0;
+}
+
+static void max7360_keypad_remove(struct platform_device *pdev)
+{
+ dev_pm_clear_wake_irq(&pdev->dev);
+}
+
+static struct platform_driver max7360_keypad_driver = {
+ .driver = {
+ .name = MAX7360_DRVNAME_KEYPAD,
+ },
+ .probe = max7360_keypad_probe,
+ .remove = max7360_keypad_remove,
+};
+module_platform_driver(max7360_keypad_driver);
+
+MODULE_DESCRIPTION("MAX7360 Keypad driver");
+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
+MODULE_ALIAS("platform:" MAX7360_DRVNAME_KEYPAD);
+MODULE_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
` (4 preceding siblings ...)
2024-12-23 16:42 ` [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad Mathieu Dubois-Briand
@ 2024-12-23 16:42 ` Mathieu Dubois-Briand
2024-12-31 17:49 ` Christophe JAILLET
2024-12-23 16:42 ` [PATCH v2 7/7] MAINTAINERS: Add entry on MAX7360 driver Mathieu Dubois-Briand
2024-12-23 17:05 ` [PATCH v2 0/7] Add support for MAX7360 Uwe Kleine-König
7 siblings, 1 reply; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
Add driver for Maxim Integrated MAX7360 rotary encoder controller,
supporting a single rotary switch.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/input/misc/Kconfig | 11 +++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/max7360-rotary.c | 185 ++++++++++++++++++++++++++++++++++++
3 files changed, 197 insertions(+)
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6a852c76331b..8430aaf08c04 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -230,6 +230,17 @@ config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
+config INPUT_MAX7360_ROTARY
+ tristate "Maxim MAX7360 Rotary Encoder"
+ depends on I2C
+ depends on MFD_MAX7360
+ help
+ If you say yes here you get support for the rotary encoder on the
+ Maxim MAX7360 I/O Expander.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max7360_rotary.
+
config INPUT_MAX77650_ONKEY
tristate "Maxim MAX77650 ONKEY support"
depends on MFD_MAX77650
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4f7f736831ba..0ed447543e43 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o
obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
diff --git a/drivers/input/misc/max7360-rotary.c b/drivers/input/misc/max7360-rotary.c
new file mode 100644
index 000000000000..4639c73a3f9d
--- /dev/null
+++ b/drivers/input/misc/max7360-rotary.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Bootlin
+ *
+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max7360.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct max7360_rotary {
+ u32 axis;
+ struct input_dev *input;
+ unsigned int debounce_ms;
+ struct regmap *regmap;
+};
+
+static irqreturn_t max7360_rotary_irq(int irq, void *data)
+{
+ struct max7360_rotary *max7360_rotary = data;
+ int val;
+ int ret;
+
+ ret = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val);
+ if (ret < 0) {
+ dev_err(&max7360_rotary->input->dev,
+ "Failed to read rotary counter");
+ return IRQ_NONE;
+ }
+
+ if (val == 0) {
+ dev_dbg(&max7360_rotary->input->dev,
+ "Got a spurious interrupt");
+
+ return IRQ_NONE;
+ }
+
+ input_report_rel(max7360_rotary->input, max7360_rotary->axis,
+ (int8_t)val);
+ input_sync(max7360_rotary->input);
+
+ return IRQ_HANDLED;
+}
+
+static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary)
+{
+ int val;
+ int ret;
+
+ ret = regmap_write_bits(max7360_rotary->regmap, MAX7360_REG_GPIOCFG,
+ MAX7360_GPIO_CFG_RTR_EN,
+ MAX7360_GPIO_CFG_RTR_EN);
+ if (ret) {
+ dev_err(&max7360_rotary->input->dev,
+ "Failed to enable max7360 rotary encoder");
+ return ret;
+ }
+
+ val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) |
+ FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY;
+ ret = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val);
+ if (ret) {
+ dev_err(&max7360_rotary->input->dev,
+ "Failed to set max7360 rotary encoder configuration");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max7360_rotary_probe(struct platform_device *pdev)
+{
+ struct max7360_rotary *max7360_rotary;
+ struct input_dev *input;
+ int irq;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return dev_err_probe(&pdev->dev, -ENODEV, "No parent device\n");
+
+ ret = max7360_port_pin_request(pdev->dev.parent, MAX7360_PORT_RTR_PIN,
+ true);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "Could not request rotary pin\n");
+
+ irq = platform_get_irq_byname(to_platform_device(pdev->dev.parent),
+ "inti");
+ if (irq < 0)
+ return irq;
+
+ max7360_rotary = devm_kzalloc(&pdev->dev, sizeof(*max7360_rotary),
+ GFP_KERNEL);
+ if (!max7360_rotary)
+ return -ENOMEM;
+
+ max7360_rotary->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!max7360_rotary->regmap)
+ dev_err_probe(&pdev->dev, -ENODEV,
+ "Could not get parent regmap\n");
+
+ device_property_read_u32(pdev->dev.parent, "linux,axis",
+ &max7360_rotary->axis);
+ device_property_read_u32(pdev->dev.parent, "rotary-debounce-delay-ms",
+ &max7360_rotary->debounce_ms);
+ if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Invalid debounce timing: %u\n",
+ max7360_rotary->debounce_ms);
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "Failed to allocate input device\n");
+
+ max7360_rotary->input = input;
+
+ input->id.bustype = BUS_I2C;
+ input->name = pdev->name;
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_REL, max7360_rotary->axis);
+ input_set_drvdata(input, max7360_rotary);
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ max7360_rotary_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
+ "max7360-rotary", max7360_rotary);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register interrupt: %d\n", ret);
+
+ ret = input_register_device(input);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Could not register input device: %d\n",
+ ret);
+
+ platform_set_drvdata(pdev, max7360_rotary);
+
+ device_init_wakeup(&pdev->dev, true);
+ ret = dev_pm_set_wake_irq(&pdev->dev, irq);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set up wakeup irq: %d\n", ret);
+
+ ret = max7360_rotary_hw_init(max7360_rotary);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to initialize max7360 rotary\n");
+
+ return 0;
+}
+
+static void max7360_rotary_remove(struct platform_device *pdev)
+{
+ dev_pm_clear_wake_irq(&pdev->dev);
+
+ /*
+ * Free the MAX7360_PORT_RTR_PIN pin, so it can be requested later by
+ * this driver, the MAX7360 GPIO driver or the MAX7360 PWM driver.
+ */
+ max7360_port_pin_request(pdev->dev.parent, MAX7360_PORT_RTR_PIN, false);
+}
+
+static struct platform_driver max7360_rotary_driver = {
+ .driver = {
+ .name = MAX7360_DRVNAME_ROTARY,
+ },
+ .probe = max7360_rotary_probe,
+ .remove = max7360_rotary_remove,
+};
+module_platform_driver(max7360_rotary_driver);
+
+MODULE_DESCRIPTION("MAX7360 Rotary driver");
+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
+MODULE_ALIAS("platform:" MAX7360_DRVNAME_ROTARY);
+MODULE_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 7/7] MAINTAINERS: Add entry on MAX7360 driver
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
` (5 preceding siblings ...)
2024-12-23 16:42 ` [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary Mathieu Dubois-Briand
@ 2024-12-23 16:42 ` Mathieu Dubois-Briand
2024-12-23 17:05 ` [PATCH v2 0/7] Add support for MAX7360 Uwe Kleine-König
7 siblings, 0 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 16:42 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni, Mathieu Dubois-Briand
Add myself as maintainer of Maxim MAX7360 driver and device-tree bindings.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
MAINTAINERS | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index baf0eeb9a355..18a7b7cf0b60 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14132,6 +14132,18 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/temperature/max30208.c
+MAXIM MAX7360 KEYPAD LED MFD DRIVER
+M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+S: Maintained
+F: Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
+F: Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
+F: drivers/gpio/gpio-max7360.c
+F: drivers/input/keyboard/max7360-keypad.c
+F: drivers/input/misc/max7360-rotary.c
+F: drivers/mfd/max7360.c
+F: drivers/pwm/pwm-max7360.c
+F: include/linux/mfd/max7360.h
+
MAXIM MAX77650 PMIC MFD DRIVER
M: Bartosz Golaszewski <brgl@bgdev.pl>
L: linux-kernel@vger.kernel.org
--
2.39.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/7] Add support for MAX7360
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
` (6 preceding siblings ...)
2024-12-23 16:42 ` [PATCH v2 7/7] MAINTAINERS: Add entry on MAX7360 driver Mathieu Dubois-Briand
@ 2024-12-23 17:05 ` Uwe Kleine-König
2024-12-23 17:09 ` Uwe Kleine-König
7 siblings, 1 reply; 23+ messages in thread
From: Uwe Kleine-König @ 2024-12-23 17:05 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, devicetree, linux-kernel, linux-gpio,
linux-input, linux-pwm, Grégory Clement, Thomas Petazzoni
[-- Attachment #1: Type: text/plain, Size: 463 bytes --]
Hello Mathieu,
On Mon, Dec 23, 2024 at 05:42:32PM +0100, Mathieu Dubois-Briand wrote:
> - Removing device tree subnodes for keypad, rotary encoder and pwm
> functionalities.
How did you test the pwm? Just using sysfs? Without a node there is
hardly any other usage left, because you cannot pass the pwm to e.g. a
pwm-fan node. So it might be sensible to drop the nodes for keypad and
rotary encoder, but I think you better keep the pwm one.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
@ 2024-12-23 17:08 ` Uwe Kleine-König
2024-12-24 9:12 ` Krzysztof Kozlowski
1 sibling, 0 replies; 23+ messages in thread
From: Uwe Kleine-König @ 2024-12-23 17:08 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, devicetree, linux-kernel, linux-gpio,
linux-input, linux-pwm, Grégory Clement, Thomas Petazzoni
[-- Attachment #1: Type: text/plain, Size: 496 bytes --]
Hello,
just a nitpick I noticed, not a full review:
On Mon, Dec 23, 2024 at 05:42:33PM +0100, Mathieu Dubois-Briand wrote:
> +examples:
> + - |
> + gpio {
> + compatible = "maxim,max7360-gpio";
> +
> + gpio-controller;
> + #gpio-cells = <0x2>;
> + ngpios = <8>;
> + maxim,constant-current-disable = <0x06>;
> +
> + interrupt-controller;
> + #interrupt-cells = <0x2>;
> + };
I think s/ // in the line with the closing curly brace.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/7] Add support for MAX7360
2024-12-23 17:05 ` [PATCH v2 0/7] Add support for MAX7360 Uwe Kleine-König
@ 2024-12-23 17:09 ` Uwe Kleine-König
2024-12-23 17:53 ` Mathieu Dubois-Briand
0 siblings, 1 reply; 23+ messages in thread
From: Uwe Kleine-König @ 2024-12-23 17:09 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, devicetree, linux-kernel, linux-gpio,
linux-input, linux-pwm, Grégory Clement, Thomas Petazzoni
[-- Attachment #1: Type: text/plain, Size: 614 bytes --]
Hello Mathieu,
On Mon, Dec 23, 2024 at 06:05:39PM +0100, Uwe Kleine-König wrote:
> On Mon, Dec 23, 2024 at 05:42:32PM +0100, Mathieu Dubois-Briand wrote:
> > - Removing device tree subnodes for keypad, rotary encoder and pwm
> > functionalities.
>
> How did you test the pwm? Just using sysfs? Without a node there is
> hardly any other usage left, because you cannot pass the pwm to e.g. a
> pwm-fan node. So it might be sensible to drop the nodes for keypad and
> rotary encoder, but I think you better keep the pwm one.
I think I was to quick here. It might just work ...
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/7] Add support for MAX7360
2024-12-23 17:09 ` Uwe Kleine-König
@ 2024-12-23 17:53 ` Mathieu Dubois-Briand
0 siblings, 0 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-23 17:53 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, devicetree, linux-kernel, linux-gpio,
linux-input, linux-pwm, Grégory Clement, Thomas Petazzoni
On Mon Dec 23, 2024 at 6:09 PM CET, Uwe Kleine-König wrote:
> Hello Mathieu,
>
> On Mon, Dec 23, 2024 at 06:05:39PM +0100, Uwe Kleine-König wrote:
> > On Mon, Dec 23, 2024 at 05:42:32PM +0100, Mathieu Dubois-Briand wrote:
> > > - Removing device tree subnodes for keypad, rotary encoder and pwm
> > > functionalities.
> >
> > How did you test the pwm? Just using sysfs? Without a node there is
> > hardly any other usage left, because you cannot pass the pwm to e.g. a
> > pwm-fan node. So it might be sensible to drop the nodes for keypad and
> > rotary encoder, but I think you better keep the pwm one.
>
> I think I was to quick here. It might just work ...
>
> Best regards
> Uwe
Hi Uwe,
I also had some doubt here, keeping the node might be bit more clear but
I thought you wanted me to drop it.
And yes, as you said, it does work. For reference, I test it using some
pwm-led:
pwm-leds {
compatible = "pwm-leds";
battery {
label = "battery";
pwms = <&max7360 0 2000000 0>;
max-brightness = <128>;
linux,default-trigger = "heartbeat";
};
};
Where &max7360 is a reference to the root node (io-expander@38 in the
binding example).
Best regards,
Mathieu
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
2024-12-23 17:08 ` Uwe Kleine-König
@ 2024-12-24 9:12 ` Krzysztof Kozlowski
2024-12-24 12:22 ` Mathieu Dubois-Briand
1 sibling, 1 reply; 23+ messages in thread
From: Krzysztof Kozlowski @ 2024-12-24 9:12 UTC (permalink / raw)
To: Mathieu Dubois-Briand, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara, Linus Walleij,
Bartosz Golaszewski, Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
On 23/12/2024 17:42, Mathieu Dubois-Briand wrote:
> Add device tree bindings for Maxim Integrated MAX7360 device with
> support for keypad, rotary, gpios and pwm functionalities.
>
> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> ---
> .../bindings/gpio/maxim,max7360-gpio.yaml | 80 +++++++++++++++
> .../devicetree/bindings/mfd/maxim,max7360.yaml | 107 +++++++++++++++++++++
> 2 files changed, 187 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
> new file mode 100644
> index 000000000000..6e6133ce6e68
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
> @@ -0,0 +1,80 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim MAX7360 GPIO controller
> +
> +maintainers:
> + - Kamel Bouhara <kamel.bouhara@bootlin.com>
> + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> +
> +description: |
> + Maxim MAX7360 GPIO controller, in MAX7360 chipset
> + https://www.analog.com/en/products/max7360.html
Don't send new versions so fast, especially for larger patchsets, so we
can finish previous discussion.
You gave me yesterday around 1 hour to respond to your last email and
then you sent v2.
Please implement my last comments on v1.
> +
> +properties:
> + compatible:
> + enum:
> + - maxim,max7360-gpio
> + - maxim,max7360-gpo
...
> +additionalProperties: false
> +
> +examples:
> + - |
> + gpio {
> + compatible = "maxim,max7360-gpio";
> +
> + gpio-controller;
> + #gpio-cells = <0x2>;
Not a hex, <2>
> + ngpios = <8>;
> + maxim,constant-current-disable = <0x06>;
> +
> + interrupt-controller;
> + #interrupt-cells = <0x2>;
Not a hex, <2>
> + };
> diff --git a/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
> new file mode 100644
> index 000000000000..1f761707070a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller
> +
> +maintainers:
> + - Kamel Bouhara <kamel.bouhara@bootlin.com>
> + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> +
> +description: |
> + Maxim MAX7360 device, with following functions:
> + - keypad controller
> + - rotary controller
> + - GPIO and GPO controller
> + - PWM controller
> +
> + https://www.analog.com/en/products/max7360.html
> +
> +allOf:
> + - $ref: /schemas/input/matrix-keymap.yaml#
> + - $ref: /schemas/input/input.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - maxim,max7360
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + minItems: 2
You can drop minItems.
> + maxItems: 2
> +> + interrupt-names:
> + items:
> + - const: inti
> + - const: intk
> +
> + keypad-debounce-delay-ms:
> + description: Keypad debounce delay in ms
> + minimum: 9
> + maximum: 40
> + default: 9
> +
> + autorepeat: true
> +
> + rotary-debounce-delay-ms:
> + description: Rotary encoder debounce delay in ms
> + minimum: 0
> + maximum: 15
> + default: 0
> +
> + linux,axis:
> + description: The input subsystem axis to map to this rotary encoder.
> +
> + "#pwm-cells":
> + const: 3
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-names
> + - linux,keymap
> + - linux,axis
> + - "#pwm-cells"
> +
> +unevaluatedProperties: false
> +
Well, I still see it incomplete... and to prove it, please post your DTS
for entire max7360 and validate it against bindings.
There is no way this works, unless GPIO is not part of this device but
then it is obviously incorrect design.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360
2024-12-24 9:12 ` Krzysztof Kozlowski
@ 2024-12-24 12:22 ` Mathieu Dubois-Briand
0 siblings, 0 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2024-12-24 12:22 UTC (permalink / raw)
To: Krzysztof Kozlowski, Lee Jones, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
On Tue Dec 24, 2024 at 10:12 AM CET, Krzysztof Kozlowski wrote:
> On 23/12/2024 17:42, Mathieu Dubois-Briand wrote:
> > Add device tree bindings for Maxim Integrated MAX7360 device with
> > support for keypad, rotary, gpios and pwm functionalities.
> >
> > Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> > ---
> > .../bindings/gpio/maxim,max7360-gpio.yaml | 80 +++++++++++++++
> > .../devicetree/bindings/mfd/maxim,max7360.yaml | 107 +++++++++++++++++++++
> > 2 files changed, 187 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
> > new file mode 100644
> > index 000000000000..6e6133ce6e68
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
> > @@ -0,0 +1,80 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Maxim MAX7360 GPIO controller
> > +
> > +maintainers:
> > + - Kamel Bouhara <kamel.bouhara@bootlin.com>
> > + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> > +
> > +description: |
> > + Maxim MAX7360 GPIO controller, in MAX7360 chipset
> > + https://www.analog.com/en/products/max7360.html
>
> Don't send new versions so fast, especially for larger patchsets, so we
> can finish previous discussion.
>
> You gave me yesterday around 1 hour to respond to your last email and
> then you sent v2.
>
Soddy, I didn't mean to. I thought having a v2 would be easier to show
how I addressed the previous comments, but I definitely was too fast. I
will make sure to wait more next time.
> Please implement my last comments on v1.
>
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - maxim,max7360-gpio
> > + - maxim,max7360-gpo
>
>
> ...
>
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + gpio {
> > + compatible = "maxim,max7360-gpio";
> > +
> > + gpio-controller;
> > + #gpio-cells = <0x2>;
>
> Not a hex, <2>
>
> > + ngpios = <8>;
> > + maxim,constant-current-disable = <0x06>;
> > +
> > + interrupt-controller;
> > + #interrupt-cells = <0x2>;
>
> Not a hex, <2>
>
Ok, I will fix both values.
> > + };
> > diff --git a/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
> > new file mode 100644
> > index 000000000000..1f761707070a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
> > @@ -0,0 +1,107 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller
> > +
> > +maintainers:
> > + - Kamel Bouhara <kamel.bouhara@bootlin.com>
> > + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
> > +
> > +description: |
> > + Maxim MAX7360 device, with following functions:
> > + - keypad controller
> > + - rotary controller
> > + - GPIO and GPO controller
> > + - PWM controller
> > +
> > + https://www.analog.com/en/products/max7360.html
> > +
> > +allOf:
> > + - $ref: /schemas/input/matrix-keymap.yaml#
> > + - $ref: /schemas/input/input.yaml#
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - maxim,max7360
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + minItems: 2
>
> You can drop minItems.
>
Ok, fixed.
> > + maxItems: 2
> > +> + interrupt-names:
> > + items:
> > + - const: inti
> > + - const: intk
> > +
> > + keypad-debounce-delay-ms:
> > + description: Keypad debounce delay in ms
> > + minimum: 9
> > + maximum: 40
> > + default: 9
> > +
> > + autorepeat: true
> > +
> > + rotary-debounce-delay-ms:
> > + description: Rotary encoder debounce delay in ms
> > + minimum: 0
> > + maximum: 15
> > + default: 0
> > +
> > + linux,axis:
> > + description: The input subsystem axis to map to this rotary encoder.
> > +
> > + "#pwm-cells":
> > + const: 3
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - interrupt-names
> > + - linux,keymap
> > + - linux,axis
> > + - "#pwm-cells"
> > +
> > +unevaluatedProperties: false
> > +
>
> Well, I still see it incomplete... and to prove it, please post your DTS
> for entire max7360 and validate it against bindings.
>
> There is no way this works, unless GPIO is not part of this device but
> then it is obviously incorrect design.
>
Ok, it looks like I completely missed how it was supposed to be
described. So, if I got it right, I need to:
- Add two properties, gpio and gpo, with $ref: /schemas/gpio/maxim,max7360-gpio.yaml#
- Add the two gpio and gpo child nodes in the example.
>
> Best regards,
> Krzysztof
Thanks for your review,
Mathieu
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support
2024-12-23 16:42 ` [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support mathieu.dubois-briand
@ 2024-12-31 17:31 ` Christophe JAILLET
0 siblings, 0 replies; 23+ messages in thread
From: Christophe JAILLET @ 2024-12-31 17:31 UTC (permalink / raw)
To: mathieu.dubois-briand, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara, Linus Walleij,
Bartosz Golaszewski, Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
Le 23/12/2024 à 17:42, mathieu.dubois-briand@bootlin.com a écrit :
> From: Kamel Bouhara <kamel.bouhara@bootlin.com>
>
> Add driver for Maxim Integrated MAX7360 PWM controller, supporting up to
> 8 independent PWM outputs.
>
> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
...
> +static int max7360_pwm_probe(struct platform_device *pdev)
> +{
> + struct max7360_pwm *max7360_pwm;
> + struct pwm_chip *chip;
> + int ret;
> +
> + if (!pdev->dev.parent)
> + return dev_err_probe(&pdev->dev, -ENODEV, "no parent device\n");
> +
> + chip = devm_pwmchip_alloc(pdev->dev.parent, MAX7360_NUM_PWMS,
> + sizeof(*max7360_pwm));
> + if (IS_ERR(chip))
> + return PTR_ERR(chip);
> + chip->ops = &max7360_pwm_ops;
> +
> + max7360_pwm = to_max7360_pwm(chip);
> + max7360_pwm->parent = pdev->dev.parent;
> +
> + max7360_pwm->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> + if (!max7360_pwm->regmap)
> + return dev_err_probe(&pdev->dev, -ENODEV,
> + "could not get parent regmap\n");
> +
> + ret = devm_pwmchip_add(&pdev->dev, chip);
> + if (ret != 0)
> + dev_err_probe(&pdev->dev, ret, "failed to add PWM chip");
Missing return, or done on purpose?
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2024-12-23 16:42 ` [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support Mathieu Dubois-Briand
@ 2024-12-31 17:40 ` Christophe JAILLET
2025-01-21 15:20 ` Andy Shevchenko
1 sibling, 0 replies; 23+ messages in thread
From: Christophe JAILLET @ 2024-12-31 17:40 UTC (permalink / raw)
To: Mathieu Dubois-Briand, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara, Linus Walleij,
Bartosz Golaszewski, Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
Le 23/12/2024 à 17:42, Mathieu Dubois-Briand a écrit :
> Add driver for Maxim Integrated MAX7360 GPIO/GPO controller.
>
> Two sets of GPIOs are provided by the device:
> - Up to 8 GPIOs, shared with the PWM and rotary encoder functionalities.
> These GPIOs also provide interrupts on input changes.
> - Up to 6 GPOs, on unused keypad columns pins.
>
> Co-developed-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
...
> +static int max7360_gpio_probe(struct platform_device *pdev)
> +{
> + struct max7360_gpio *max7360_gpio;
> + struct platform_device *parent;
> + unsigned int ngpios;
> + unsigned int outconf;
> + struct gpio_irq_chip *girq;
> + unsigned long flags;
> + int irq;
> + int ret;
> +
> + if (!pdev->dev.parent) {
> + dev_err(&pdev->dev, "no parent device\n");
> + return -ENODEV;
> + }
> + parent = to_platform_device(pdev->dev.parent);
> +
> + max7360_gpio = devm_kzalloc(&pdev->dev, sizeof(struct max7360_gpio),
sizeof(*max7360_gpio)?
> + GFP_KERNEL);
> + if (!max7360_gpio)
> + return -ENOMEM;
> +
> + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
> + dev_err(&pdev->dev, "Missing ngpios OF property\n");
> + return -ENODEV;
> + }
> +
> + max7360_gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> + if (!max7360_gpio->regmap) {
> + dev_err(&pdev->dev, "could not get parent regmap\n");
> + return -ENODEV;
> + }
> +
> + max7360_gpio->dev = &pdev->dev;
> + max7360_gpio->chip = max7360_gpio_chip;
> + max7360_gpio->chip.ngpio = ngpios;
> + max7360_gpio->chip.parent = &pdev->dev;
> + max7360_gpio->chip.base = -1;
> + max7360_gpio->gpio_function = (uintptr_t)device_get_match_data(&pdev->dev);
> +
> + dev_dbg(&pdev->dev, "gpio count: %d\n", max7360_gpio->chip.ngpio);
> +
> + if (max7360_gpio->gpio_function == MAX7360_GPIO_PORT) {
> + /* Port GPIOs: set output mode configuration (constant-current
> + * or not).
> + * This property is optional.
> + */
> + outconf = 0;
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "maxim,constant-current-disable",
> + &outconf);
> + if (ret && (ret != -EINVAL)) {
> + dev_err(&pdev->dev,
> + "Failed to read maxim,constant-current-disable OF property\n");
> + return -ENODEV;
> + }
> +
> + regmap_write(max7360_gpio->regmap, MAX7360_REG_GPIOOUTM, outconf);
> + }
> +
> + if (max7360_gpio->gpio_function == MAX7360_GPIO_PORT &&
> + of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) {
> + /* Port GPIOs: declare IRQ chip, if configuration was provided.
> + */
> + irq = platform_get_irq_byname(parent, "inti");
> + if (irq < 0)
> + return dev_err_probe(&pdev->dev, irq,
> + "Failed to get IRQ");
> +
> + flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED;
> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> + max7360_gpio_irq, flags,
> + "max7360-gpio", max7360_gpio);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "Failed to register interrupt: %d\n",
> + ret);
No need to duplicate ret in the message.
> +
> + girq = &max7360_gpio->chip.irq;
> + gpio_irq_chip_set_chip(girq, &max7360_gpio_irqchip);
> + girq->parent_handler = NULL;
> + girq->num_parents = 0;
> + girq->parents = NULL;
> + girq->threaded = true;
> + girq->default_type = IRQ_TYPE_NONE;
> + girq->handler = handle_simple_irq;
> + }
> +
> + ret = devm_gpiochip_add_data(&pdev->dev, &max7360_gpio->chip, max7360_gpio);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
return dev_err_probe()?
> + return ret;
> + }
> +
> + return 0;
> +}
CJ
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad
2024-12-23 16:42 ` [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad Mathieu Dubois-Briand
@ 2024-12-31 17:45 ` Christophe JAILLET
0 siblings, 0 replies; 23+ messages in thread
From: Christophe JAILLET @ 2024-12-31 17:45 UTC (permalink / raw)
To: Mathieu Dubois-Briand, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara, Linus Walleij,
Bartosz Golaszewski, Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
Le 23/12/2024 à 17:42, Mathieu Dubois-Briand a écrit :
> Add driver for Maxim Integrated MAX7360 keypad controller, providing
> support for up to 64 keys, with a matrix of 8 columns and 8 rows.
>
> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
...
> +static int max7360_keypad_open(struct input_dev *pdev)
> +{
> + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
> + int error;
> +
> + /*
> + * Somebody is using the device: get out of sleep.
> + */
> + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,
> + MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP);
> + if (error) {
> + dev_err(&max7360_keypad->input->dev,
> + "Failed to write max7360 configuration");
Missing \n.
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static void max7360_keypad_close(struct input_dev *pdev)
> +{
> + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
> + int error;
> +
> + /*
> + * Nobody is using the device anymore: go to sleep.
> + */
> + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,
> + MAX7360_CFG_SLEEP, ~MAX7360_CFG_SLEEP);
> + if (error)
> + dev_err(&max7360_keypad->input->dev,
> + "Failed to write max7360 configuration");
Missing \n.
> +}
> +
> +static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad)
> +{
> + unsigned int val;
> + int error;
> +
> + val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN;
> + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE,
> + MAX7360_DEBOUNCE,
> + FIELD_PREP(MAX7360_DEBOUNCE, val));
> + if (error) {
> + dev_err(&max7360_keypad->input->dev,
> + "Failed to write max7360 debounce configuration");
Missing \n.
This is called from probe. Use dev_err_rpobe()?
> + return error;
> + }
> +
> + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT,
> + MAX7360_INTERRUPT_TIME_MASK,
> + FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1));
> + if (error) {
> + dev_err(&max7360_keypad->input->dev,
> + "Failed to write max7360 keypad interrupt configuration");
Missing \n.
This is called from probe. Use dev_err_rpobe()?
> + return error;
> + }
> +
> + return 0;
> +}
...
> +static int max7360_keypad_probe(struct platform_device *pdev)
> +{
> + struct max7360_keypad *max7360_keypad;
> + struct input_dev *input;
> + bool autorepeat;
> + int error;
> + int irq;
> +
> + if (!pdev->dev.parent)
> + return dev_err_probe(&pdev->dev, -ENODEV, "No parent device\n");
> +
> + irq = platform_get_irq_byname(to_platform_device(pdev->dev.parent),
> + "intk");
> + if (irq < 0)
> + return irq;
> +
> + max7360_keypad = devm_kzalloc(&pdev->dev, sizeof(*max7360_keypad),
> + GFP_KERNEL);
> + if (!max7360_keypad)
> + return -ENOMEM;
> +
> + max7360_keypad->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> + if (!max7360_keypad->regmap)
> + return dev_err_probe(&pdev->dev, -ENODEV,
> + "Could not get parent regmap\n");
> +
> + error = max7360_keypad_parse_dt(pdev, max7360_keypad, &autorepeat);
> + if (error)
> + return error;
> +
> + input = devm_input_allocate_device(pdev->dev.parent);
> + if (!input)
> + return dev_err_probe(&pdev->dev, -ENOMEM,
> + "Failed to allocate input device\n");
Not sure an error message is needed.
> +
> + max7360_keypad->input = input;
> +
> + input->id.bustype = BUS_I2C;
> + input->name = pdev->name;
> + input->open = max7360_keypad_open;
> + input->close = max7360_keypad_close;
> +
> + error = matrix_keypad_build_keymap(NULL, NULL,
> + MAX7360_MAX_KEY_ROWS,
> + MAX7360_MAX_KEY_COLS,
> + max7360_keypad->keycodes, input);
> + if (error)
> + return dev_err_probe(&pdev->dev, error,
> + "Failed to build keymap\n");
> +
> + input_set_capability(input, EV_MSC, MSC_SCAN);
> + if (autorepeat)
> + __set_bit(EV_REP, input->evbit);
> +
> + input_set_drvdata(input, max7360_keypad);
> +
> + error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> + max7360_keypad_irq,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "max7360-keypad", max7360_keypad);
> + if (error)
> + return dev_err_probe(&pdev->dev, error,
> + "Failed to register interrupt: %d\n",
> + error);
No need to duplicate error.
> +
> + error = input_register_device(input);
> + if (error)
> + return dev_err_probe(&pdev->dev, error,
> + "Could not register input device: %d\n",
> + error);
No need to duplicate error.
> +
> + platform_set_drvdata(pdev, max7360_keypad);
> +
> + error = max7360_keypad_hw_init(max7360_keypad);
> + if (error)
> + return dev_err_probe(&pdev->dev, error,
> + "Failed to initialize max7360 keypad\n");
> +
> + device_init_wakeup(&pdev->dev, true);
> + error = dev_pm_set_wake_irq(&pdev->dev, irq);
> + if (error)
> + dev_warn(&pdev->dev, "Failed to set up wakeup irq: %d\n",
> + error);
> +
> + return 0;
> +}
CJ
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary
2024-12-23 16:42 ` [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary Mathieu Dubois-Briand
@ 2024-12-31 17:49 ` Christophe JAILLET
2025-01-02 9:34 ` Mathieu Dubois-Briand
0 siblings, 1 reply; 23+ messages in thread
From: Christophe JAILLET @ 2024-12-31 17:49 UTC (permalink / raw)
To: Mathieu Dubois-Briand, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara, Linus Walleij,
Bartosz Golaszewski, Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
Le 23/12/2024 à 17:42, Mathieu Dubois-Briand a écrit :
> Add driver for Maxim Integrated MAX7360 rotary encoder controller,
> supporting a single rotary switch.
>
> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
...
> +static irqreturn_t max7360_rotary_irq(int irq, void *data)
> +{
> + struct max7360_rotary *max7360_rotary = data;
> + int val;
> + int ret;
> +
> + ret = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val);
> + if (ret < 0) {
> + dev_err(&max7360_rotary->input->dev,
> + "Failed to read rotary counter");
Missing \n.
> + return IRQ_NONE;
> + }
> +
> + if (val == 0) {
> + dev_dbg(&max7360_rotary->input->dev,
> + "Got a spurious interrupt");
Missing \n.
> +
> + return IRQ_NONE;
> + }
> +
> + input_report_rel(max7360_rotary->input, max7360_rotary->axis,
> + (int8_t)val);
> + input_sync(max7360_rotary->input);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary)
> +{
> + int val;
> + int ret;
> +
> + ret = regmap_write_bits(max7360_rotary->regmap, MAX7360_REG_GPIOCFG,
> + MAX7360_GPIO_CFG_RTR_EN,
> + MAX7360_GPIO_CFG_RTR_EN);
> + if (ret) {
> + dev_err(&max7360_rotary->input->dev,
> + "Failed to enable max7360 rotary encoder");
Missing \n.
> + return ret;
> + }
> +
> + val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) |
> + FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY;
> + ret = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val);
> + if (ret) {
> + dev_err(&max7360_rotary->input->dev,
> + "Failed to set max7360 rotary encoder configuration");
Missing \n.
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int max7360_rotary_probe(struct platform_device *pdev)
> +{
> + struct max7360_rotary *max7360_rotary;
> + struct input_dev *input;
> + int irq;
> + int ret;
> +
> + if (!pdev->dev.parent)
> + return dev_err_probe(&pdev->dev, -ENODEV, "No parent device\n");
> +
> + ret = max7360_port_pin_request(pdev->dev.parent, MAX7360_PORT_RTR_PIN,
> + true);
> + if (ret)
> + dev_err_probe(&pdev->dev, ret,
> + "Could not request rotary pin\n");
Missing return or done on purpose?
> +
> + irq = platform_get_irq_byname(to_platform_device(pdev->dev.parent),
> + "inti");
> + if (irq < 0)
> + return irq;
> +
> + max7360_rotary = devm_kzalloc(&pdev->dev, sizeof(*max7360_rotary),
> + GFP_KERNEL);
> + if (!max7360_rotary)
> + return -ENOMEM;
> +
> + max7360_rotary->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> + if (!max7360_rotary->regmap)
> + dev_err_probe(&pdev->dev, -ENODEV,
> + "Could not get parent regmap\n");
> +
> + device_property_read_u32(pdev->dev.parent, "linux,axis",
> + &max7360_rotary->axis);
> + device_property_read_u32(pdev->dev.parent, "rotary-debounce-delay-ms",
> + &max7360_rotary->debounce_ms);
> + if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX)
> + return dev_err_probe(&pdev->dev, -EINVAL,
> + "Invalid debounce timing: %u\n",
> + max7360_rotary->debounce_ms);
> +
> + input = devm_input_allocate_device(&pdev->dev);
> + if (!input)
> + return dev_err_probe(&pdev->dev, -ENOMEM,
> + "Failed to allocate input device\n");
Not sure an error message is needed.
> +
> + max7360_rotary->input = input;
> +
> + input->id.bustype = BUS_I2C;
> + input->name = pdev->name;
> + input->dev.parent = &pdev->dev;
> +
> + input_set_capability(input, EV_REL, max7360_rotary->axis);
> + input_set_drvdata(input, max7360_rotary);
> +
> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> + max7360_rotary_irq,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
> + "max7360-rotary", max7360_rotary);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "Failed to register interrupt: %d\n", ret);
No need to duplicate ret.
> +
> + ret = input_register_device(input);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "Could not register input device: %d\n",
> + ret);
No need to duplicate ret.
> +
> + platform_set_drvdata(pdev, max7360_rotary);
> +
> + device_init_wakeup(&pdev->dev, true);
> + ret = dev_pm_set_wake_irq(&pdev->dev, irq);
> + if (ret)
> + dev_warn(&pdev->dev, "Failed to set up wakeup irq: %d\n", ret);
> +
> + ret = max7360_rotary_hw_init(max7360_rotary);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "Failed to initialize max7360 rotary\n");
> +
> + return 0;
> +}
CJ
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary
2024-12-31 17:49 ` Christophe JAILLET
@ 2025-01-02 9:34 ` Mathieu Dubois-Briand
0 siblings, 0 replies; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2025-01-02 9:34 UTC (permalink / raw)
To: Christophe JAILLET, Lee Jones, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
Grégory Clement, Thomas Petazzoni
On Tue Dec 31, 2024 at 6:49 PM CET, Christophe JAILLET wrote:
> Le 23/12/2024 à 17:42, Mathieu Dubois-Briand a écrit :
> > Add driver for Maxim Integrated MAX7360 rotary encoder controller,
> > supporting a single rotary switch.
> >
> > Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
>
> ...
>
>
> CJ
Hi Christophe,
Thanks a lot for all your comments. I've integrated fixes for all of
them, to be shipped with the next version.
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2024-12-23 16:42 ` [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support Mathieu Dubois-Briand
2024-12-31 17:40 ` Christophe JAILLET
@ 2025-01-21 15:20 ` Andy Shevchenko
2025-01-22 13:04 ` Mathieu Dubois-Briand
1 sibling, 1 reply; 23+ messages in thread
From: Andy Shevchenko @ 2025-01-21 15:20 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, devicetree, linux-kernel,
linux-gpio, linux-input, linux-pwm, Grégory Clement,
Thomas Petazzoni
On Mon, Dec 23, 2024 at 05:42:36PM +0100, Mathieu Dubois-Briand wrote:
> Add driver for Maxim Integrated MAX7360 GPIO/GPO controller.
>
> Two sets of GPIOs are provided by the device:
> - Up to 8 GPIOs, shared with the PWM and rotary encoder functionalities.
> These GPIOs also provide interrupts on input changes.
> - Up to 6 GPOs, on unused keypad columns pins.
May I ask if you researched for the existing drivers that may host this without
much to modify?
Second, please, avoid OF-centric APIs in a new code. Use fwnode and/or device
property APIs. (This stands for the whole series and any new contributions)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2025-01-21 15:20 ` Andy Shevchenko
@ 2025-01-22 13:04 ` Mathieu Dubois-Briand
2025-01-22 16:31 ` Andy Shevchenko
0 siblings, 1 reply; 23+ messages in thread
From: Mathieu Dubois-Briand @ 2025-01-22 13:04 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, devicetree, linux-kernel,
linux-gpio, linux-input, linux-pwm, Grégory Clement,
Thomas Petazzoni
On Tue Jan 21, 2025 at 4:20 PM CET, Andy Shevchenko wrote:
> On Mon, Dec 23, 2024 at 05:42:36PM +0100, Mathieu Dubois-Briand wrote:
Hi Andy,
Thanks for your review. I'm not sure you have seen it, but there is a v3
of this series. V3 of this patch can be seen here:
https://lore.kernel.org/all/20250113-mdb-max7360-support-v3-4-9519b4acb0b1@bootlin.com/
Yet, your comments are still valid for the v3.
> > Add driver for Maxim Integrated MAX7360 GPIO/GPO controller.
> >
> > Two sets of GPIOs are provided by the device:
> > - Up to 8 GPIOs, shared with the PWM and rotary encoder functionalities.
> > These GPIOs also provide interrupts on input changes.
> > - Up to 6 GPOs, on unused keypad columns pins.
>
> May I ask if you researched for the existing drivers that may host this without
> much to modify?
>
I did had a look at the existing drivers and in particular at all
gpio-max* drivers. Unfortunately, I believe none of them target chipsets
similar to the MAX7360.
The only similarity I noted, is with the MAX732x chipsets, on the IRQ
side: both lack of dedicated bits for each line, leading to some logic
to try to recover the corresponding lines on interrupt. My code is
partly based on the MAX732x code on this specific point.
> Second, please, avoid OF-centric APIs in a new code. Use fwnode and/or device
> property APIs. (This stands for the whole series and any new contributions)
Yes, I definitely need to remove these of_property_read_*() calls. I'm
going to replace them with device_property_read_*() calls.
Best regards,
Mathieu
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2025-01-22 13:04 ` Mathieu Dubois-Briand
@ 2025-01-22 16:31 ` Andy Shevchenko
2025-01-27 13:08 ` Andy Shevchenko
0 siblings, 1 reply; 23+ messages in thread
From: Andy Shevchenko @ 2025-01-22 16:31 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, devicetree, linux-kernel,
linux-gpio, linux-input, linux-pwm, Grégory Clement,
Thomas Petazzoni
On Wed, Jan 22, 2025 at 02:04:35PM +0100, Mathieu Dubois-Briand wrote:
> On Tue Jan 21, 2025 at 4:20 PM CET, Andy Shevchenko wrote:
...
> Thanks for your review. I'm not sure you have seen it, but there is a v3
> of this series. V3 of this patch can be seen here:
> https://lore.kernel.org/all/20250113-mdb-max7360-support-v3-4-9519b4acb0b1@bootlin.com/
Thanks for sharing, I will look at it later this on next week.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support
2025-01-22 16:31 ` Andy Shevchenko
@ 2025-01-27 13:08 ` Andy Shevchenko
0 siblings, 0 replies; 23+ messages in thread
From: Andy Shevchenko @ 2025-01-27 13:08 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, devicetree, linux-kernel,
linux-gpio, linux-input, linux-pwm, Grégory Clement,
Thomas Petazzoni
On Wed, Jan 22, 2025 at 06:31:10PM +0200, Andy Shevchenko wrote:
> On Wed, Jan 22, 2025 at 02:04:35PM +0100, Mathieu Dubois-Briand wrote:
> > On Tue Jan 21, 2025 at 4:20 PM CET, Andy Shevchenko wrote:
...
> > Thanks for your review. I'm not sure you have seen it, but there is a v3
> > of this series. V3 of this patch can be seen here:
> > https://lore.kernel.org/all/20250113-mdb-max7360-support-v3-4-9519b4acb0b1@bootlin.com/
>
> Thanks for sharing, I will look at it later this on next week.
Just answered to the GPIO patch in it. Waiting for v4...
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-01-27 13:08 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-23 16:42 [PATCH v2 0/7] Add support for MAX7360 Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 1/7] dt-bindings: mfd: gpio: Add MAX7360 Mathieu Dubois-Briand
2024-12-23 17:08 ` Uwe Kleine-König
2024-12-24 9:12 ` Krzysztof Kozlowski
2024-12-24 12:22 ` Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 2/7] mfd: Add max7360 support mathieu.dubois-briand
2024-12-23 16:42 ` [PATCH v2 3/7] pwm: max7360: Add MAX7360 PWM support mathieu.dubois-briand
2024-12-31 17:31 ` Christophe JAILLET
2024-12-23 16:42 ` [PATCH v2 4/7] gpio: max7360: Add MAX7360 gpio support Mathieu Dubois-Briand
2024-12-31 17:40 ` Christophe JAILLET
2025-01-21 15:20 ` Andy Shevchenko
2025-01-22 13:04 ` Mathieu Dubois-Briand
2025-01-22 16:31 ` Andy Shevchenko
2025-01-27 13:08 ` Andy Shevchenko
2024-12-23 16:42 ` [PATCH v2 5/7] input: keyboard: Add support for MAX7360 keypad Mathieu Dubois-Briand
2024-12-31 17:45 ` Christophe JAILLET
2024-12-23 16:42 ` [PATCH v2 6/7] input: misc: Add support for MAX7360 rotary Mathieu Dubois-Briand
2024-12-31 17:49 ` Christophe JAILLET
2025-01-02 9:34 ` Mathieu Dubois-Briand
2024-12-23 16:42 ` [PATCH v2 7/7] MAINTAINERS: Add entry on MAX7360 driver Mathieu Dubois-Briand
2024-12-23 17:05 ` [PATCH v2 0/7] Add support for MAX7360 Uwe Kleine-König
2024-12-23 17:09 ` Uwe Kleine-König
2024-12-23 17:53 ` Mathieu Dubois-Briand
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).