* [PATCH v13 07/10] gpio: max7360: Add MAX7360 gpio support
From: Mathieu Dubois-Briand @ 2025-08-11 10:46 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, Michael Walle, Mark Brown,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
andriy.shevchenko, Grégory Clement, Thomas Petazzoni,
Mathieu Dubois-Briand, Bartosz Golaszewski, Andy Shevchenko
In-Reply-To: <20250811-mdb-max7360-support-v13-0-e79fcabff386@bootlin.com>
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>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
drivers/gpio/Kconfig | 12 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max7360.c | 257 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e43abb322fa6..6cf57a18dbe7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1492,6 +1492,18 @@ config GPIO_MADERA
help
Support for GPIOs on Cirrus Logic Madera class codecs.
+config GPIO_MAX7360
+ tristate "MAX7360 GPIO support"
+ depends on MFD_MAX7360
+ select GPIO_REGMAP
+ select REGMAP_IRQ
+ 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 379f55e9ed1e..52abba3ef81c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -106,6 +106,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_MAX77759) += gpio-max77759.o
diff --git a/drivers/gpio/gpio-max7360.c b/drivers/gpio/gpio-max7360.c
new file mode 100644
index 000000000000..db92a43776a9
--- /dev/null
+++ b/drivers/gpio/gpio-max7360.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Bootlin
+ *
+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>
+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max7360.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#define MAX7360_GPIO_PORT 1
+#define MAX7360_GPIO_COL 2
+
+struct max7360_gpio_plat_data {
+ unsigned int function;
+};
+
+static struct max7360_gpio_plat_data max7360_gpio_port_plat = { .function = MAX7360_GPIO_PORT };
+static struct max7360_gpio_plat_data max7360_gpio_col_plat = { .function = MAX7360_GPIO_COL };
+
+static int max7360_get_available_gpos(struct device *dev, unsigned int *available_gpios)
+{
+ u32 columns;
+ int ret;
+
+ ret = device_property_read_u32(dev->parent, "keypad,num-columns", &columns);
+ if (ret) {
+ dev_err(dev, "Failed to read columns count\n");
+ return ret;
+ }
+
+ *available_gpios = min(MAX7360_MAX_GPO, MAX7360_MAX_KEY_COLS - columns);
+
+ return 0;
+}
+
+static int max7360_gpo_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ unsigned int available_gpios;
+ int ret;
+
+ ret = max7360_get_available_gpos(gc->parent, &available_gpios);
+ if (ret)
+ return ret;
+
+ bitmap_clear(valid_mask, 0, MAX7360_MAX_KEY_COLS - available_gpios);
+
+ return 0;
+}
+
+static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap)
+{
+ /*
+ * MAX7360 COL0 to COL7 pins can be used either as keypad columns,
+ * general purpose output or a mix of both.
+ * By default, all pins are used as keypad, here we update this
+ * configuration to allow to use some of them as GPIOs.
+ */
+ unsigned int available_gpios;
+ unsigned int val;
+ int ret;
+
+ ret = max7360_get_available_gpos(dev, &available_gpios);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure which GPIOs will be used for keypad.
+ * 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, available_gpios);
+ ret = regmap_write_bits(regmap, MAX7360_REG_DEBOUNCE, MAX7360_PORTS, val);
+ if (ret)
+ dev_err(dev, "Failed to write max7360 columns/gpos configuration");
+
+ return ret;
+}
+
+static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
+{
+ if (base == MAX7360_REG_PWMBASE) {
+ /*
+ * GPIO output is using PWM duty cycle registers: one register
+ * per line, with value being either 0 or 255.
+ */
+ *reg = base + offset;
+ *mask = GENMASK(7, 0);
+ } else {
+ *reg = base;
+ *mask = BIT(offset);
+ }
+
+ return 0;
+}
+
+static const struct regmap_irq max7360_regmap_irqs[MAX7360_MAX_GPIO] = {
+ REGMAP_IRQ_REG(0, 0, BIT(0)),
+ REGMAP_IRQ_REG(1, 0, BIT(1)),
+ REGMAP_IRQ_REG(2, 0, BIT(2)),
+ REGMAP_IRQ_REG(3, 0, BIT(3)),
+ REGMAP_IRQ_REG(4, 0, BIT(4)),
+ REGMAP_IRQ_REG(5, 0, BIT(5)),
+ REGMAP_IRQ_REG(6, 0, BIT(6)),
+ REGMAP_IRQ_REG(7, 0, BIT(7)),
+};
+
+static int max7360_handle_mask_sync(const int index,
+ const unsigned int mask_buf_def,
+ const unsigned int mask_buf,
+ void *const irq_drv_data)
+{
+ struct regmap *regmap = irq_drv_data;
+ int ret;
+
+ for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
+ ret = regmap_assign_bits(regmap, MAX7360_REG_PWMCFG(i),
+ MAX7360_PORT_CFG_INTERRUPT_MASK, mask_buf & BIT(i));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max7360_gpio_probe(struct platform_device *pdev)
+{
+ const struct max7360_gpio_plat_data *plat_data;
+ struct gpio_regmap_config gpio_config = { };
+ struct regmap_irq_chip *irq_chip;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ unsigned int outconf;
+ int ret;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return dev_err_probe(dev, -ENODEV, "could not get parent regmap\n");
+
+ plat_data = device_get_match_data(dev);
+ if (plat_data->function == MAX7360_GPIO_PORT) {
+ if (device_property_read_bool(dev, "interrupt-controller")) {
+ /*
+ * Port GPIOs with interrupt-controller property: add IRQ
+ * controller.
+ */
+ gpio_config.regmap_irq_flags = IRQF_ONESHOT | IRQF_SHARED;
+ gpio_config.regmap_irq_line =
+ fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti");
+ if (gpio_config.regmap_irq_line < 0)
+ return dev_err_probe(dev, gpio_config.regmap_irq_line,
+ "Failed to get IRQ\n");
+
+ /* Create custom IRQ configuration. */
+ irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
+ gpio_config.regmap_irq_chip = irq_chip;
+ if (!irq_chip)
+ return -ENOMEM;
+
+ irq_chip->name = dev_name(dev);
+ irq_chip->status_base = MAX7360_REG_GPIOIN;
+ irq_chip->status_is_level = true;
+ irq_chip->num_regs = 1;
+ irq_chip->num_irqs = MAX7360_MAX_GPIO;
+ irq_chip->irqs = max7360_regmap_irqs;
+ irq_chip->handle_mask_sync = max7360_handle_mask_sync;
+ irq_chip->irq_drv_data = regmap;
+
+ for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
+ ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
+ MAX7360_PORT_CFG_INTERRUPT_EDGES,
+ MAX7360_PORT_CFG_INTERRUPT_EDGES);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable interrupts\n");
+ }
+ }
+
+ /*
+ * Port GPIOs: set output mode configuration (constant-current or not).
+ * This property is optional.
+ */
+ ret = device_property_read_u32(dev, "maxim,constant-current-disable", &outconf);
+ if (!ret) {
+ ret = regmap_write(regmap, MAX7360_REG_GPIOOUTM, outconf);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set constant-current configuration\n");
+ }
+ }
+
+ /* Add gpio device. */
+ gpio_config.parent = dev;
+ gpio_config.regmap = regmap;
+ if (plat_data->function == MAX7360_GPIO_PORT) {
+ gpio_config.ngpio = MAX7360_MAX_GPIO;
+ gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOIN);
+ gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PWMBASE);
+ gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOCTRL);
+ gpio_config.ngpio_per_reg = MAX7360_MAX_GPIO;
+ gpio_config.reg_mask_xlate = max7360_gpio_reg_mask_xlate;
+ } else {
+ ret = max7360_set_gpos_count(dev, regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set GPOS pin count\n");
+
+ gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PORTS);
+ gpio_config.ngpio = MAX7360_MAX_KEY_COLS;
+ gpio_config.init_valid_mask = max7360_gpo_init_valid_mask;
+ }
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
+}
+
+static const struct of_device_id max7360_gpio_of_match[] = {
+ {
+ .compatible = "maxim,max7360-gpo",
+ .data = &max7360_gpio_col_plat
+ }, {
+ .compatible = "maxim,max7360-gpio",
+ .data = &max7360_gpio_port_plat
+ }, {
+ }
+};
+MODULE_DEVICE_TABLE(of, max7360_gpio_of_match);
+
+static struct platform_driver max7360_gpio_driver = {
+ .driver = {
+ .name = "max7360-gpio",
+ .of_match_table = 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_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related
* [PATCH v13 02/10] mfd: Add max7360 support
From: Mathieu Dubois-Briand @ 2025-08-11 10:46 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, Michael Walle, Mark Brown,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
andriy.shevchenko, Grégory Clement, Thomas Petazzoni,
Mathieu Dubois-Briand, Andy Shevchenko
In-Reply-To: <20250811-mdb-max7360-support-v13-0-e79fcabff386@bootlin.com>
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>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
drivers/mfd/Kconfig | 14 ++++
drivers/mfd/Makefile | 1 +
drivers/mfd/max7360.c | 171 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/max7360.h | 109 ++++++++++++++++++++++++++++
4 files changed, 295 insertions(+)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 425c5fba6cb1..58b1c2900d59 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2481,5 +2481,19 @@ config MFD_UPBOARD_FPGA
To compile this driver as a module, choose M here: the module will be
called upboard-fpga.
+config MFD_MAX7360
+ tristate "Maxim MAX7360 I2C IO Expander"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for Maxim MAX7360 device, embedding
+ keypad, rotary encoder, PWM and GPIO features.
+
+ 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 f7bdedd5a66d..c81c6a8473e1 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..5ee459c490ec
--- /dev/null
+++ b/drivers/mfd/max7360.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim MAX7360 Core Driver
+ *
+ * Copyright 2025 Bootlin
+ *
+ * Authors:
+ * Kamel Bouhara <kamel.bouhara@bootlin.com>
+ * Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max7360.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+static const struct mfd_cell max7360_cells[] = {
+ { .name = "max7360-pinctrl" },
+ { .name = "max7360-pwm" },
+ { .name = "max7360-keypad" },
+ { .name = "max7360-rotary" },
+ {
+ .name = "max7360-gpo",
+ .of_compatible = "maxim,max7360-gpo",
+ },
+ {
+ .name = "max7360-gpio",
+ .of_compatible = "maxim,max7360-gpio",
+ },
+};
+
+static const struct regmap_range max7360_volatile_ranges[] = {
+ regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO),
+ regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT),
+};
+
+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 = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1),
+ .volatile_table = &max7360_volatile_table,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int max7360_mask_irqs(struct regmap *regmap)
+{
+ struct device *dev = regmap_get_device(regmap);
+ unsigned int val;
+ int ret;
+
+ /*
+ * GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI"
+ * interrupt line is shared between GPIOs and rotary encoder, this could
+ * result in repeated spurious interrupts on the rotary encoder driver
+ * if the GPIO driver is not loaded. Mask them now to avoid this
+ * situation.
+ */
+ for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {
+ ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
+ MAX7360_PORT_CFG_INTERRUPT_MASK,
+ MAX7360_PORT_CFG_INTERRUPT_MASK);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to write MAX7360 port configuration\n");
+ }
+
+ /* Read GPIO in register, to ACK any pending IRQ. */
+ ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read GPIO values\n");
+
+ return 0;
+}
+
+static int max7360_reset(struct regmap *regmap)
+{
+ struct device *dev = regmap_get_device(regmap);
+ int ret;
+
+ ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST);
+ if (ret) {
+ dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret);
+ return ret;
+ }
+
+ ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST);
+ if (ret) {
+ dev_err(dev, "Failed to drop regmap cache: %x\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0);
+ if (ret) {
+ dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0);
+ if (ret)
+ dev_err(dev, "Failed to reset GPO port count: %x\n", ret);
+
+ return ret;
+}
+
+static int max7360_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int ret;
+
+ 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");
+
+ ret = max7360_reset(regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ /* Get the device out of shutdown mode. */
+ ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,
+ MAX7360_GPIO_CFG_GPIO_EN,
+ MAX7360_GPIO_CFG_GPIO_EN);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n");
+
+ ret = max7360_mask_irqs(regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Could not mask interrupts\n");
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ max7360_cells, ARRAY_SIZE(max7360_cells),
+ NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "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 I2C IO Expander 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..44cf2bf651a2
--- /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/bits.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 GPIO registers
+ *
+ * All these registers are reset together when writing bit 3 of
+ * MAX7360_REG_GPIOCFG.
+ */
+#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_I2C_TIMEOUT 0x48
+#define MAX7360_REG_GPIOIN 0x49
+#define MAX7360_REG_RTR_CNT 0x4A
+#define MAX7360_REG_PWMBASE 0x50
+#define MAX7360_REG_PWMCFGBASE 0x58
+
+#define MAX7360_REG_GPIO_LAST 0x5F
+
+#define MAX7360_REG_PWM(x) (MAX7360_REG_PWMBASE + (x))
+#define MAX7360_REG_PWMCFG(x) (MAX7360_REG_PWMCFGBASE + (x))
+
+/*
+ * 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_PORT_CFG_COMMON_PWM BIT(5)
+
+/*
+ * Autosleep register values
+ */
+#define MAX7360_AUTOSLEEP_8192MS 0x01
+#define MAX7360_AUTOSLEEP_4096MS 0x02
+#define MAX7360_AUTOSLEEP_2048MS 0x03
+#define MAX7360_AUTOSLEEP_1024MS 0x04
+#define MAX7360_AUTOSLEEP_512MS 0x05
+#define MAX7360_AUTOSLEEP_256MS 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
+
+#endif
--
2.39.5
^ permalink raw reply related
* [PATCH v13 01/10] dt-bindings: mfd: gpio: Add MAX7360
From: Mathieu Dubois-Briand @ 2025-08-11 10:46 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, Michael Walle, Mark Brown,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
andriy.shevchenko, Grégory Clement, Thomas Petazzoni,
Mathieu Dubois-Briand, Krzysztof Kozlowski
In-Reply-To: <20250811-mdb-max7360-support-v13-0-e79fcabff386@bootlin.com>
Add device tree bindings for Maxim Integrated MAX7360 device with
support for keypad, rotary, gpios and pwm functionalities.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
.../bindings/gpio/maxim,max7360-gpio.yaml | 83 +++++++++
.../devicetree/bindings/mfd/maxim,max7360.yaml | 191 +++++++++++++++++++++
2 files changed, 274 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..c5c3fc4c816f
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
@@ -0,0 +1,83 @@
+# 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
+
+ The device provides two series of GPIOs, referred here as GPIOs and GPOs.
+
+ PORT0 to PORT7 pins can be used as GPIOs, with support for interrupts and
+ constant-current mode. These pins will also be used by the rotary encoder and
+ PWM functionalities.
+
+ COL2 to COL7 pins can be used as GPOs, there is no input capability. COL pins
+ will be partitioned, with the first pins being affected to the keypad
+ functionality and the last ones as GPOs.
+
+properties:
+ compatible:
+ enum:
+ - maxim,max7360-gpio
+ - maxim,max7360-gpo
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
+ 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.
+ maximum: 0xff
+
+required:
+ - compatible
+ - gpio-controller
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - maxim,max7360-gpio
+ ngpios: false
+ then:
+ required:
+ - interrupt-controller
+ else:
+ properties:
+ interrupt-controller: false
+ maxim,constant-current-disable: false
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio {
+ compatible = "maxim,max7360-gpio";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ maxim,constant-current-disable = <0x06>;
+
+ interrupt-controller;
+ #interrupt-cells = <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..3fc920c8639d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
@@ -0,0 +1,191 @@
+# 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:
+ 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
+
+ rotary-debounce-delay-ms:
+ description: Rotary encoder debounce delay in ms
+ minimum: 0
+ maximum: 15
+ default: 0
+
+ linux,axis:
+ $ref: /schemas/input/rotary-encoder.yaml#/properties/linux,axis
+
+ rotary-encoder,relative-axis:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ Register a relative axis rather than an absolute one.
+
+ rotary-encoder,steps:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 24
+ description:
+ Number of steps in a full turnaround of the
+ encoder. Only relevant for absolute axis. Defaults to 24 which is a
+ typical value for such devices.
+
+ rotary-encoder,rollover:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ Automatic rollover when the rotary value becomes
+ greater than the specified steps or smaller than 0. For absolute axis only.
+
+ "#pwm-cells":
+ const: 3
+
+ gpio:
+ $ref: /schemas/gpio/maxim,max7360-gpio.yaml#
+ description:
+ PORT0 to PORT7 general purpose input/output pins configuration.
+
+ gpo:
+ $ref: /schemas/gpio/maxim,max7360-gpio.yaml#
+ description: >
+ COL2 to COL7 general purpose output pins configuration. Allows to use
+ unused keypad columns as outputs.
+
+ The MAX7360 has 8 column lines and 6 of them can be used as GPOs. GPIOs
+ numbers used for this gpio-controller node do correspond to the column
+ numbers: values 0 and 1 are never valid, values from 2 to 7 might be valid
+ depending on the value of the keypad,num-column property.
+
+patternProperties:
+ '-pins$':
+ type: object
+ description:
+ Pinctrl node's client devices use subnodes for desired pin configuration.
+ Client device subnodes use below standard properties.
+ $ref: /schemas/pinctrl/pincfg-node.yaml
+
+ properties:
+ pins:
+ description:
+ List of gpio pins affected by the properties specified in this
+ subnode.
+ items:
+ pattern: '^(PORT[0-7]|ROTARY)$'
+ minItems: 1
+ maxItems: 8
+
+ function:
+ description:
+ Specify the alternative function to be configured for the specified
+ pins.
+ enum: [gpio, pwm, rotary]
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - linux,keymap
+ - linux,axis
+ - "#pwm-cells"
+ - gpio
+ - gpo
+
+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 */
+ rotary-encoder,relative-axis;
+
+ #pwm-cells = <3>;
+
+ max7360_gpio: gpio {
+ compatible = "maxim,max7360-gpio";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ maxim,constant-current-disable = <0x06>;
+
+ interrupt-controller;
+ #interrupt-cells = <0x2>;
+ };
+
+ max7360_gpo: gpo {
+ compatible = "maxim,max7360-gpo";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ backlight_pins: backlight-pins {
+ pins = "PORT2";
+ function = "pwm";
+ };
+ };
+ };
--
2.39.5
^ permalink raw reply related
* [PATCH v13 00/10] Add support for MAX7360
From: Mathieu Dubois-Briand @ 2025-08-11 10:46 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
Dmitry Torokhov, Uwe Kleine-König, Michael Walle, Mark Brown,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
andriy.shevchenko, Grégory Clement, Thomas Petazzoni,
Mathieu Dubois-Briand, Krzysztof Kozlowski, Andy Shevchenko,
Bartosz Golaszewski
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.
Lee Jones suggested the whole series goes through MFD subsystem, once
all patches got the needed Acks.
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
---
Changes in v13:
- Rebased on v6.17-rc1.
- PWM: fixed return value on rounding.
- Link to v12: https://lore.kernel.org/r/20250722-mdb-max7360-support-v12-0-3747721a8d02@bootlin.com
Changes in v12:
- Rebased on v6.16-rc6.
- PWM: fixed rounding rules.
- PWM: added a link to the datasheet and fixed case in two error
messages.
- Link to v11: https://lore.kernel.org/r/20250711-mdb-max7360-support-v11-0-cf1dee2a7d4c@bootlin.com
Changes in v11:
- Rebased on v6.16-rc5.
- Small fixes in keypad and rotary encoder input drivers: typos and off
by one errors.
- Various fixes in PWM driver and PWM Kconfig.
- Link to v10: https://lore.kernel.org/r/20250530-mdb-max7360-support-v10-0-ce3b9e60a588@bootlin.com
Changes in v10:
- Rebased on v6.15
- Do not use devm_ functions to allocate regmap-irq in gpio-remap.c
- Link to v9: https://lore.kernel.org/r/20250522-mdb-max7360-support-v9-0-74fc03517e41@bootlin.com
Changes in v9:
- Fix build issue with bad usage of array_size() on intermediate commit.
- MFD: Fix error strings. Also fix #define style in the header file.
- Pinctrl: Fix missing include.
- PWM: Fix register writes in max7360_pwm_waveform() and
max7360_pwm_round_waveform_tohw().
- GPIO: Fix GPIO valid mask initialization.
- Link to v8: https://lore.kernel.org/r/20250509-mdb-max7360-support-v8-0-bbe486f6bcb7@bootlin.com
Changes in v8:
- Small changes in drivers.
- Rebased on v6.15-rc5
- Link to v7: https://lore.kernel.org/r/20250428-mdb-max7360-support-v7-0-4e0608d0a7ff@bootlin.com
Changes in v7:
- Add rotary encoder absolute axis support in device tree bindings and
driver.
- Lot of small changes in keypad, rotary encoder and GPIO drivers.
- Rebased on v6.15-rc4
- Link to v6: https://lore.kernel.org/r/20250409-mdb-max7360-support-v6-0-7a2535876e39@bootlin.com
Changes in v6:
- Rebased on v6.15-rc1.
- Use device_set_of_node_from_dev() instead of creating PWM and Pinctrl
on parent device.
- Various small fixes in all drivers.
- Fix pins property pattern in pinctrl dt bindings.
- Link to v5: https://lore.kernel.org/r/20250318-mdb-max7360-support-v5-0-fb20baf97da0@bootlin.com
Changes in v5:
- Add pinctrl driver to replace the previous use of request()/free()
callbacks for PORT pins.
- Dropping Reviewed-by tags on device-tree binding commit, because of
modifications related to the previous point.
- Remove ngpios property from GPIO device tree bindings.
- Use GPIO valid_mask to mark unusable keypad columns GPOs, instead of
changing ngpios.
- Drop patches adding support for request()/free() callbacks in GPIO
regmap and gpio_regmap_get_ngpio().
- Allow gpio_regmap_register() to create the associated regmap IRQ.
- Various fixes in MFD, PWM, GPIO and KEYPAD drivers.
- Link to v4: https://lore.kernel.org/r/20250214-mdb-max7360-support-v4-0-8a35c6dbb966@bootlin.com
Changes in v4:
- Modified the GPIO driver to use gpio-regmap and regmap-irq.
- Add support for request()/free() callbacks in gpio-regmap.
- Add support for status_is_level in regmap-irq.
- Switched the PWM driver to waveform callbacks.
- Various small fixes in MFD, PWM, GPIO drivers and dt bindings.
- Rebased on v6.14-rc2.
- Link to v3: https://lore.kernel.org/r/20250113-mdb-max7360-support-v3-0-9519b4acb0b1@bootlin.com
Changes in v3:
- Fix MFD device tree binding to add gpio child nodes.
- Fix various small issues in device tree bindings.
- Add missing line returns in error messages.
- Use dev_err_probe() when possible.
- Link to v2: https://lore.kernel.org/r/20241223-mdb-max7360-support-v2-0-37a8d22c36ed@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 (8):
dt-bindings: mfd: gpio: Add MAX7360
pinctrl: Add MAX7360 pinctrl driver
gpio: regmap: Allow to allocate regmap-irq device
gpio: regmap: Allow to provide init_valid_mask callback
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 | 83 ++++++
.../devicetree/bindings/mfd/maxim,max7360.yaml | 191 +++++++++++++
MAINTAINERS | 13 +
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max7360.c | 257 +++++++++++++++++
drivers/gpio/gpio-regmap.c | 30 +-
drivers/input/keyboard/Kconfig | 12 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/max7360-keypad.c | 308 +++++++++++++++++++++
drivers/input/misc/Kconfig | 10 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/max7360-rotary.c | 192 +++++++++++++
drivers/mfd/Kconfig | 14 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max7360.c | 171 ++++++++++++
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-max7360.c | 215 ++++++++++++++
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-max7360.c | 209 ++++++++++++++
include/linux/gpio/regmap.h | 18 ++
include/linux/mfd/max7360.h | 109 ++++++++
24 files changed, 1869 insertions(+), 2 deletions(-)
---
base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
change-id: 20241219-mdb-max7360-support-223a8ce45ba3
Best regards,
--
Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
^ permalink raw reply
* Re: [PATCH 09/21] input: gpio-keys: make legacy gpiolib optional
From: Matti Vaittinen @ 2025-08-11 10:34 UTC (permalink / raw)
To: Arnd Bergmann, Bartosz Golaszewski, Linus Walleij, linux-gpio,
Dmitry Torokhov, Lee Jones
Cc: Arnd Bergmann, Gatien Chevallier, Fabrice Gasnier,
Andy Shevchenko, Bartosz Golaszewski, Thomas Gleixner,
Charles Keepax, Krzysztof Kozlowski, Christophe JAILLET,
linux-input, linux-kernel
In-Reply-To: <20250808151822.536879-10-arnd@kernel.org>
Hi dee Ho peeps,
On 08/08/2025 18:17, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> Most users of gpio-keys and gpio-keys-polled use modern gpiolib
> interfaces, but there are still number of ancient sh, arm32 and x86
> machines that have never been converted.
>
> Add an #ifdef block for the parts of the driver that are only
> used on those legacy machines.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> drivers/input/keyboard/gpio_keys.c | 5 +++--
> drivers/input/keyboard/gpio_keys_polled.c | 2 ++
> drivers/mfd/rohm-bd71828.c | 2 ++
> drivers/mfd/rohm-bd718x7.c | 2 ++
> include/linux/gpio_keys.h | 2 ++
> 5 files changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
> index f9db86da0818..984b20f773ed 100644
> --- a/drivers/input/keyboard/gpio_keys.c
> +++ b/drivers/input/keyboard/gpio_keys.c
> @@ -528,6 +528,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
> */
> bdata->gpiod = NULL;
> }
> +#ifdef CONFIG_GPIOLIB_LEGACY
> } else if (gpio_is_valid(button->gpio)) {
> /*
> * Legacy GPIO number, so request the GPIO here and
> @@ -546,6 +547,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
>
> if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
> gpiod_toggle_active_low(bdata->gpiod);
> +#endif
> }
>
> if (bdata->gpiod) {
> @@ -583,8 +585,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
> if (irq < 0) {
> error = irq;
> dev_err_probe(dev, error,
> - "Unable to get irq number for GPIO %d\n",
> - button->gpio);
> + "Unable to get irq number for GPIO\n");
> return error;
> }
> bdata->irq = irq;
> diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
> index e6707d72210e..0ae0e53910ea 100644
> --- a/drivers/input/keyboard/gpio_keys_polled.c
> +++ b/drivers/input/keyboard/gpio_keys_polled.c
> @@ -301,6 +301,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
> return dev_err_probe(dev, PTR_ERR(bdata->gpiod),
> "failed to get gpio\n");
> }
> +#ifdef CONFIG_GPIOLIB_LEGACY
> } else if (gpio_is_valid(button->gpio)) {
> /*
> * Legacy GPIO number so request the GPIO here and
> @@ -323,6 +324,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
>
> if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
> gpiod_toggle_active_low(bdata->gpiod);
> +#endif
> }
>
> bdata->last_state = -1;
> diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
> index a14b7aa69c3c..fb68694fadca 100644
> --- a/drivers/mfd/rohm-bd71828.c
> +++ b/drivers/mfd/rohm-bd71828.c
> @@ -21,7 +21,9 @@
>
> static struct gpio_keys_button button = {
> .code = KEY_POWER,
> +#ifdef CONFIG_GPIOLIB_LEGACY
> .gpio = -1,
> +#endif
> .type = EV_KEY,
> };
>
> diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
> index 25e494a93d48..6c99ab62e31b 100644
> --- a/drivers/mfd/rohm-bd718x7.c
> +++ b/drivers/mfd/rohm-bd718x7.c
> @@ -20,7 +20,9 @@
>
> static struct gpio_keys_button button = {
> .code = KEY_POWER,
> +#ifdef CONFIG_GPIOLIB_LEGACY
> .gpio = -1,
> +#endif
> .type = EV_KEY,
> };
>
> diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
> index 80fa930b04c6..e8d6dc290efb 100644
> --- a/include/linux/gpio_keys.h
> +++ b/include/linux/gpio_keys.h
> @@ -25,7 +25,9 @@ struct device;
> */
> struct gpio_keys_button {
> unsigned int code;
> +#ifdef CONFIG_GPIOLIB_LEGACY
> int gpio;
> +#endif
> int active_low;
> const char *desc;
> unsigned int type;
AFAIR, these ROHM PMICs (bd718[15, 27, 28, 37, 47, 50, 78, 85]) all
provide a 'button IRQ', from a power button. (Or, couple of IRQs but
let's skip the details) The gpio-keys is used to send the KEY_POWER
event when IRQ is detected.
The IRQ comes from the PMIC, and the regmap_irq chip provided by the MFD
provides it. This IRQ information is delivered to the gpio-keys from the
MFD driver via platform data. That's basically what these "button"
structs are here for. No GPIO line information (only the IRQ number) is
needed to be delivered to the gpio-keys. This problematic assignment:
> +#ifdef CONFIG_GPIOLIB_LEGACY
> .gpio = -1,
> +#endif
is only needed to invalidate the gpio information so that the gpio-keys
wont use it, only the IRQ.
As such, this patch seems Ok to me, you can treat this as an ack :)
This, however made me ponder following - is this the tight way to handle
the power-button IRQ? I don't see any other MFD devices doing this in
same way, although I am pretty sure there are other PMICs with similar
power-button IRQ...
I see for example the "drivers/mfd/rt5120.c" to invoke
"drivers/input/misc/rt5120-pwrkey.c" instead of using the gpio-keys.
This, however, feels like code duplication to me. I'd rather kept using
the gpio-keys, but seeing:
git grep KEY_POWER drivers/mfd/
drivers/mfd/rohm-bd71828.c: .code = KEY_POWER,
drivers/mfd/rohm-bd718x7.c: .code = KEY_POWER,
makes me wonder if there is more widely used (better) way?
Yours,
-- Matti
^ permalink raw reply
* Re: 答复: [External Mail][PATCH v2 2/2] HID: input: report battery status changes immediately
From: José Expósito @ 2025-08-11 10:30 UTC (permalink / raw)
To: 卢国宏
Cc: jikos@kernel.org, bentiss@kernel.org, linux-input@vger.kernel.org,
linux-kernel@vger.kernel.org, Fei1 Jiang 蒋飞
In-Reply-To: <726471d1e4774348bd62ecf289a5a307@xiaomi.com>
Hi 卢国宏,
The mailing list won't accept your emails unless you send them
in plain text format. Forwarding it for awareness:
On Mon, Aug 11, 2025 at 04:23:55AM +0000, 卢国宏 wrote:
>
> Hello, José!
> When I submitted your two changes to Google's Android GKI (The patch I compiled is: https://android-review.googlesource.com/c/kernel/common/+/3723411), they raised two issues:
> 1. This patch has no functional changes. Why is a cherry-pick needed?
> 2. FROMGIT patches must cite the source repository, branch, and sha. Please see https://android.googlesource.com/kernel/common/+/refs/heads/android-mainline/README.md
>
> From their documentation, I learned that they recommend submitting the changes as follows:
> Requirements for other backports: FROMGIT:, FROMLIST:,
> If the patch has been merged into an upstream maintainer tree, but has not yet been merged into Linux mainline
> tag the patch subject with FROMGIT:
> add info on where the patch came from as (cherry picked from commit <sha1> <repo> <branch>). This must be a stable maintainer branch (not rebased, so don't use linux-next for example).
> if changes were required, use BACKPORT: FROMGIT:
> Example:
> if the commit message in the maintainer tree is
> important patch from upstream
>
> This is the detailed description of the important patch
>
> Signed-off-by: Fred Jones <fred.jones@foo.org>
> then Joe Smith would upload the patch for the common kernel as
> FROMGIT: important patch from upstream
>
> This is the detailed description of the important patch
>
> Signed-off-by: Fred Jones <fred.jones@foo.org>
>
> Bug: 135791357
> (cherry picked from commit 878a2fd9de10b03d11d2f622250285c7e63deace
> https://git.kernel.org/pub/scm/linux/kernel/git/foo/bar.git test-branch)
> Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01
> Signed-off-by: Joe Smith <joe.smith@foo.org>
> However, I didn't find the information Google mentioned in your email: the source repo, branch, and sha.
> Have you submitted these two patches to the kernel tree? Could you please provide a patch with the information Google needs? Thank you very much!
The patches are not merged yet, that's why you can not find the commit
SHA that you need.
A maintainer will send an email to the ML once the patches are reviewed
and accepted, but they are very busy, so it'll take some time.
Jose
> ________________________________
> 发件人: José Expósito <jose.exposito89@gmail.com>
> 发送时间: 2025年8月6日 15:39
> 收件人: jikos@kernel.org
> 抄送: bentiss@kernel.org; 卢国宏; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; José Expósito
> 主题: [External Mail][PATCH v2 2/2] HID: input: report battery status changes immediately
>
> [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
>
> When the battery status changes, report the change immediately to user
> space.
>
> Fixes: a608dc1c0639 ("HID: input: map battery system charging")
> Reported-by: 卢国宏 <luguohong@xiaomi.com>
> Closes: https://lore.kernel.org/linux-input/aI49Im0sGb6fpgc8@fedora/T/
> Tested-by: 卢国宏 <luguohong@xiaomi.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> ---
> drivers/hid/hid-input.c | 23 ++++++++++-------------
> 1 file changed, 10 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index 262787e6eb20..f45f856a127f 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -609,13 +609,19 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> return false;
> }
>
> -static void hidinput_update_battery(struct hid_device *dev, int value)
> +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> + int value)
> {
> int capacity;
>
> if (!dev->battery)
> return;
>
> + if (hidinput_update_battery_charge_status(dev, usage, value)) {
> + power_supply_changed(dev->battery);
> + return;
> + }
> +
> if (value == 0 || value < dev->battery_min || value > dev->battery_max)
> return;
>
> @@ -642,13 +648,8 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
> {
> }
>
> -static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> - unsigned int usage, int value)
> -{
> - return false;
> -}
> -
> -static void hidinput_update_battery(struct hid_device *dev, int value)
> +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> + int value)
> {
> }
> #endif /* CONFIG_HID_BATTERY_STRENGTH */
> @@ -1515,11 +1516,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
> return;
>
> if (usage->type == EV_PWR) {
> - bool handled = hidinput_update_battery_charge_status(hid, usage->hid, value);
> -
> - if (!handled)
> - hidinput_update_battery(hid, value);
> -
> + hidinput_update_battery(hid, usage->hid, value);
> return;
> }
>
> --
> 2.50.1
>
> #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#
^ permalink raw reply
* [PATCH] HID: Wacom: Add a new Art Pen
From: Ping Cheng @ 2025-08-11 5:40 UTC (permalink / raw)
To: linux-input, jkosina, bentiss; +Cc: Ping Cheng
Signed-off-by: Ping Cheng <ping.cheng@wacom.com>
---
drivers/hid/wacom_wac.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 955b39d22524..9b2c710f8da1 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -684,6 +684,7 @@ static bool wacom_is_art_pen(int tool_id)
case 0x885: /* Intuos3 Marker Pen */
case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */
+ case 0x204: /* Art Pen 2 */
is_art_pen = true;
break;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 05/21] x86/platform: select legacy gpiolib interfaces where used
From: Dmitry Torokhov @ 2025-08-11 2:27 UTC (permalink / raw)
To: Hans de Goede
Cc: Arnd Bergmann, Andy Shevchenko, Arnd Bergmann,
Bartosz Golaszewski, Linus Walleij, open list:GPIO SUBSYSTEM,
Ilpo Järvinen, Lee Jones, Dzmitry Sankouski,
Heiko Stübner, linux, Thomas Weißschuh, Guenter Roeck,
linux-input, linux-kernel, platform-driver-x86
In-Reply-To: <9bc69944-a34e-4a4e-9071-7d2049d12449@kernel.org>
Hi Hans,
On Sun, Aug 10, 2025 at 05:12:46PM +0200, Hans de Goede wrote:
> Hi Arnd, Andy,
>
> On 9-Aug-25 9:44 PM, Arnd Bergmann wrote:
> > On Sat, Aug 9, 2025, at 12:00, Andy Shevchenko wrote:
> >> On Fri, Aug 08, 2025 at 05:17:49PM +0200, Arnd Bergmann wrote:
> >>> From: Arnd Bergmann <arnd@arndb.de>
> >>>
> >>> A few old machines have not been converted away from the old-style
> >>> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
> >>> symbol so the code still works where it is needed but can be left
> >>> out otherwise.
> >>
> >>> --- a/drivers/platform/x86/x86-android-tablets/Kconfig
> >>> +++ b/drivers/platform/x86/x86-android-tablets/Kconfig
> >>> @@ -8,6 +8,7 @@ config X86_ANDROID_TABLETS
> >>> depends on I2C && SPI && SERIAL_DEV_BUS
> >>> depends on GPIOLIB && PMIC_OPREGION
> >>> depends on ACPI && EFI && PCI
> >>> + select GPIOLIB_LEGACY
> >>> select NEW_LEDS
> >>> select LEDS_CLASS
> >>> select POWER_SUPPLY
> >>
> >> Hmm... This is a surprising change. But I leave it to Hans.
>
> Yes I was surprised by this myself since I explicitly removed
> all legacy GPIO use from the x86-android-tablets code a while
> ago (or so I thought).
>
> > I think the only function that still needs it is
> > x86_android_tablet_probe() doing
> >
> > static struct gpio_keys_button *buttons;
> >
> > for (i = 0; i < dev_info->gpio_button_count; i++) {
> > ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
> > dev_info->gpio_button[i].pin,
> > dev_info->gpio_button[i].button.desc,
> > false, GPIOD_IN, &gpiod);
> >
> > buttons[i] = dev_info->gpio_button[i].button;
> > buttons[i].gpio = desc_to_gpio(gpiod);
> > /* Release GPIO descriptor so that gpio-keys can request it */
> > devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
> > }
> >
> > So the driver itself uses gpio descriptors, but it passes
> > some of them into another driver by number. There is probably
> > an easy workaround that I did not see.
>
> Ah I see, so this is basically in the same boat as
> drivers/input/misc/soc_button_array.c which also first
> gets a gpio_desc and then calls desc_to_gpio() to store
> the GPIO number in struct gpio_keys_button which is passed
> as platform_data to drivers/input/keyboard/gpio_keys.c
>
> The gpio_keys driver then converts things back
> into a gpio_desc in gpio_keys_setup_key()
> using devm_gpio_request_one() + gpio_to_desc()
>
> So it looks like we need to add a gpiod member to
> struct gpio_keys_button (include/linux/gpio_keys.h)
> and modify gpio_keys.c to prefer that over using
> button->gpio, something like the attached patch
> basically.
>
> I won't have time to work on this until September,
> so if someone wants to take the attached patch and run
> with it go for it.
>
> Note the x86-android-tablets / soc_button_array code
> will become responsible for requesting / releasing
> the gpiod when using the new gpio_keys_button.gpiod
> member.
>
> For the x86-android-tablets code this is easy, just drop
> these 2 lines:
>
> /* Release GPIO descriptor so that gpio-keys can request it */
> devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
>
> And for soc_button_array.c it is _probably_ just a matter
> of switching to devm_gpiod_get_index() and drop the
> gpiod_put().
>
> I have hardware to test both the x86-android-tablets
> code as well as the soc_button_array code. I might be
> able to do a quick test on August 22nd or 29th.
I just sent out a v2 of my series from '23 converting
x86-android-tablets to use PROPERTY_ENTRY_GPIO(), including converting
buttons and switches:
https://lore.kernel.org/all/20250810-x86-andoroid-tablet-v2-0-9c7a1b3c32b2@gmail.com/
I do not have hardware so it probably is busted but if you could make it
work that would be great.
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH] HID: lg-g15 - Add support for Logitech G13.
From: Leo L. Schwab @ 2025-08-10 22:56 UTC (permalink / raw)
To: Hans de Goede
Cc: Kate Hsuan, Leo L. Schwab, Jiri Kosina, Benjamin Tissoires,
linux-input, linux-kernel
The Logitech G13 is a gaming keypad with general-purpose macro keys,
four LED-backlit macro preset keys, five "menu" keys, backlight toggle
key, an analog thumbstick, RGB LED backlight, and a monochrome LCD
display.
This patch supports input event generation for all keys, the thumbstick,
and exposes all LEDs.
Signed-off-by: Leo L. Schwab <ewhac@ewhac.org>
---
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-lg-g15.c | 448 +++++++++++++++++++++++++++++++++++++--
2 files changed, 428 insertions(+), 21 deletions(-)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 33cc5820f2be..7ed1e402b80a 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -870,6 +870,7 @@
#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
+#define USB_DEVICE_ID_LOGITECH_G13 0xc21c
#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222
#define USB_DEVICE_ID_LOGITECH_G11 0xc225
#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index f8605656257b..cbbd20bc089c 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -17,6 +17,8 @@
#include <dt-bindings/leds/common.h>
#include "hid-ids.h"
+#include "linux/input-event-codes.h"
+#include "linux/input.h"
#define LG_G15_TRANSFER_BUF_SIZE 20
@@ -26,7 +28,11 @@
#define LG_G510_FEATURE_BACKLIGHT_RGB 0x05
#define LG_G510_FEATURE_POWER_ON_RGB 0x06
+#define LG_G13_FEATURE_M_KEYS_LEDS 0x05
+#define LG_G13_FEATURE_BACKLIGHT_RGB 0x07
+
enum lg_g15_model {
+ LG_G13,
LG_G15,
LG_G15_V2,
LG_G510,
@@ -45,6 +51,12 @@ enum lg_g15_led_type {
LG_G15_LED_MAX
};
+struct g13_input_report {
+ u8 report_id; // 1
+ u8 joy_x, joy_y;
+ u8 keybits[5];
+};
+
struct lg_g15_led {
union {
struct led_classdev cdev;
@@ -63,12 +75,182 @@ struct lg_g15_data {
struct mutex mutex;
struct work_struct work;
struct input_dev *input;
+ struct input_dev *input_js; // joystick device for G13
struct hid_device *hdev;
enum lg_g15_model model;
struct lg_g15_led leds[LG_G15_LED_MAX];
bool game_mode_enabled;
};
+/********* G13 LED functions ***********/
+/*
+ * G13 retains no state across power cycles, and always powers up with the backlight on,
+ * color #5AFF6E, all macro key LEDs off.
+ */
+static int lg_g13_get_leds_state(struct lg_g15_data *g15)
+{
+ u8 * const tbuf = g15->transfer_buf;
+ int ret, high;
+
+ /* RGB backlight. */
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error getting backlight brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ /* Normalize RGB intensities against highest component. */
+ high = max3(tbuf[1], tbuf[2], tbuf[3]);
+ if (high) {
+ g15->leds[LG_G15_KBD_BRIGHTNESS].red =
+ DIV_ROUND_CLOSEST(tbuf[1] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].green =
+ DIV_ROUND_CLOSEST(tbuf[2] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].blue =
+ DIV_ROUND_CLOSEST(tbuf[3] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = high;
+ } else {
+ g15->leds[LG_G15_KBD_BRIGHTNESS].red = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].green = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].blue = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = 0;
+ }
+
+ /* Macro LEDs. */
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error getting macro LED brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ for (int i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i)
+ g15->leds[i].brightness = tbuf[1] & (1 << (i - LG_G15_MACRO_PRESET1));
+
+ return 0;
+}
+
+/* Must be called with g15->mutex locked */
+static int lg_g13_kbd_led_write(struct lg_g15_data *g15,
+ struct lg_g15_led *g15_led,
+ enum led_brightness brightness)
+{
+ struct mc_subled const * const subleds = g15_led->mcdev.subled_info;
+ u8 * const tbuf = g15->transfer_buf;
+ int ret;
+
+ led_mc_calc_color_components(&g15_led->mcdev, brightness);
+
+ tbuf[0] = 5;
+ tbuf[1] = subleds[0].brightness;
+ tbuf[2] = subleds[1].brightness;
+ tbuf[3] = subleds[2].brightness;
+ tbuf[4] = 0;
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret == 5) {
+ g15_led->brightness = brightness;
+ ret = 0;
+ } else {
+ hid_err(g15->hdev, "Error setting backlight brightness: %d\n", ret);
+ ret = (ret < 0) ? ret : -EIO;
+ }
+
+ return ret;
+}
+
+static int lg_g13_kbd_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev);
+ struct lg_g15_led *g15_led =
+ container_of(mc, struct lg_g15_led, mcdev);
+ struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
+ int ret;
+
+ /* Ignore LED off on unregister / keyboard unplug */
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return 0;
+
+ mutex_lock(&g15->mutex);
+ ret = lg_g13_kbd_led_write(g15, g15_led, brightness);
+ mutex_unlock(&g15->mutex);
+
+ return ret;
+}
+
+static enum led_brightness lg_g13_kbd_led_get(struct led_classdev *led_cdev)
+{
+ struct led_classdev_mc const * const mc = lcdev_to_mccdev(led_cdev);
+ struct lg_g15_led const *g15_led =
+ container_of(mc, struct lg_g15_led, mcdev);
+
+ return g15_led->brightness;
+}
+
+static int lg_g13_mkey_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct lg_g15_led *g15_led =
+ container_of(led_cdev, struct lg_g15_led, cdev);
+ struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
+ int i, ret;
+ u8 * const tbuf = g15->transfer_buf;
+ u8 val, mask = 0;
+
+ /* Ignore LED off on unregister / keyboard unplug */
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return 0;
+
+ mutex_lock(&g15->mutex);
+
+ for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i) {
+ if (i == g15_led->led)
+ val = brightness;
+ else
+ val = g15->leds[i].brightness;
+
+ if (val)
+ mask |= 1 << (i - LG_G15_MACRO_PRESET1);
+ }
+
+ tbuf[0] = 5;
+ tbuf[1] = mask;
+ tbuf[2] =
+ tbuf[3] =
+ tbuf[4] = 0;
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret == 5) {
+ g15_led->brightness = brightness;
+ ret = 0;
+ } else {
+ hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
+ ret = (ret < 0) ? ret : -EIO;
+ }
+
+ mutex_unlock(&g15->mutex);
+
+ return ret;
+}
+
+static enum led_brightness lg_g13_mkey_led_get(struct led_classdev *led_cdev)
+{
+ /*
+ * G13 doesn't change macro key LEDs behind our back, so they're
+ * whatever we last set them to.
+ */
+ struct lg_g15_led *g15_led =
+ container_of(led_cdev, struct lg_g15_led, cdev);
+
+ return g15_led->brightness;
+}
+
/******** G15 and G15 v2 LED functions ********/
static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
@@ -390,6 +572,8 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
int ret;
switch (g15->model) {
+ case LG_G13:
+ return lg_g13_get_leds_state(g15);
case LG_G15:
case LG_G15_V2:
return lg_g15_update_led_brightness(g15);
@@ -417,6 +601,115 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
/******** Input functions ********/
+/**
+ * g13_input_report.keybits[] is not 32-bit aligned, so we can't use the bitops macros.
+ *
+ * @ary: Pointer to array of u8s
+ * @b: Bit index into ary, LSB first. Not range checked.
+ */
+#define TEST_BIT(ary, b) ((1 << ((b) & 7)) & (ary)[(b) >> 3])
+
+/* Table mapping keybits[] bit positions to event codes. */
+/* Note: Indices are discontinuous to aid readability. */
+static const u16 g13_keys_for_bits[] = {
+ /* Main keypad - keys G1 - G22 */
+ [0] = KEY_MACRO1,
+ [1] = KEY_MACRO2,
+ [2] = KEY_MACRO3,
+ [3] = KEY_MACRO4,
+ [4] = KEY_MACRO5,
+ [5] = KEY_MACRO6,
+ [6] = KEY_MACRO7,
+ [7] = KEY_MACRO8,
+ [8] = KEY_MACRO9,
+ [9] = KEY_MACRO10,
+ [10] = KEY_MACRO11,
+ [11] = KEY_MACRO12,
+ [12] = KEY_MACRO13,
+ [13] = KEY_MACRO14,
+ [14] = KEY_MACRO15,
+ [15] = KEY_MACRO16,
+ [16] = KEY_MACRO17,
+ [17] = KEY_MACRO18,
+ [18] = KEY_MACRO19,
+ [19] = KEY_MACRO20,
+ [20] = KEY_MACRO21,
+ [21] = KEY_MACRO22,
+
+ /* LCD menu buttons. */
+ [24] = KEY_KBD_LCD_MENU5, // "Next page" button
+ [25] = KEY_KBD_LCD_MENU1, // Left-most
+ [26] = KEY_KBD_LCD_MENU2,
+ [27] = KEY_KBD_LCD_MENU3,
+ [28] = KEY_KBD_LCD_MENU4, // Right-most
+
+ /* Macro preset and record buttons; have red LEDs under them. */
+ [29] = KEY_MACRO_PRESET1,
+ [30] = KEY_MACRO_PRESET2,
+ [31] = KEY_MACRO_PRESET3,
+ [32] = KEY_MACRO_RECORD_START,
+
+ /* 33-35 handled by joystick device. */
+
+ /* Backlight toggle. */
+ [37] = KEY_LIGHTS_TOGGLE,
+};
+
+static const u16 g13_keys_for_bits_js[] = {
+ /* Joystick buttons */
+ /* These keybits are at bit indices 33, 34, and 35. */
+ BTN_BASE, // Left side
+ BTN_BASE2, // Bottom side
+ BTN_THUMB, // Stick depress
+};
+
+static int lg_g13_event(struct lg_g15_data *g15, u8 const *data)
+{
+ struct g13_input_report const * const rep = (struct g13_input_report *) data;
+ int i, val;
+ bool hw_brightness_changed;
+
+ /*
+ * Main macropad and menu keys.
+ * Emit key events defined for each bit position.
+ */
+ for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) {
+ if (g13_keys_for_bits[i]) {
+ val = TEST_BIT(rep->keybits, i);
+ input_report_key(g15->input, g13_keys_for_bits[i], val);
+ }
+ }
+ input_sync(g15->input);
+
+ /*
+ * Joystick.
+ * Emit button and deflection events.
+ */
+ for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i) {
+ if (g13_keys_for_bits_js[i]) {
+ val = TEST_BIT(rep->keybits, i + 33);
+ input_report_key(g15->input_js, g13_keys_for_bits_js[i], val);
+ }
+ }
+ input_report_abs(g15->input_js, ABS_X, rep->joy_x);
+ input_report_abs(g15->input_js, ABS_Y, rep->joy_y);
+ input_sync(g15->input_js);
+
+ /*
+ * Bit 23 of keybits[] reports the current backlight on/off state. If
+ * it has changed from the last cached value, apply an update.
+ */
+ hw_brightness_changed =
+ (!!TEST_BIT(rep->keybits, 23)) ^ (g15->leds[0].cdev.brightness_hw_changed > 0);
+ if (hw_brightness_changed) {
+ led_classdev_notify_brightness_hw_changed(
+ &g15->leds[0].cdev,
+ TEST_BIT(rep->keybits, 23) ? LED_FULL : LED_OFF);
+ }
+
+ return 0;
+}
+
/* On the G15 Mark I Logitech has been quite creative with which bit is what */
static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data)
{
@@ -572,6 +865,10 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
switch (g15->model) {
+ case LG_G13:
+ if (data[0] == 0x01 && size == sizeof(struct g13_input_report))
+ return lg_g13_event(g15, data);
+ break;
case LG_G15:
if (data[0] == 0x02 && size == 9)
return lg_g15_event(g15, data);
@@ -616,13 +913,22 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index)
{
int i;
struct mc_subled *subled_info;
-
- g15->leds[index].mcdev.led_cdev.brightness_set_blocking =
- lg_g510_kbd_led_set;
- g15->leds[index].mcdev.led_cdev.brightness_get =
- lg_g510_kbd_led_get;
- g15->leds[index].mcdev.led_cdev.max_brightness = 255;
- g15->leds[index].mcdev.num_colors = 3;
+ struct lg_g15_led * const gled = &g15->leds[index];
+
+ if (g15->model == LG_G13) {
+ gled->mcdev.led_cdev.brightness_set_blocking =
+ lg_g13_kbd_led_set;
+ gled->mcdev.led_cdev.brightness_get =
+ lg_g13_kbd_led_get;
+ gled->mcdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED;
+ } else {
+ gled->mcdev.led_cdev.brightness_set_blocking =
+ lg_g510_kbd_led_set;
+ gled->mcdev.led_cdev.brightness_get =
+ lg_g510_kbd_led_get;
+ }
+ gled->mcdev.led_cdev.max_brightness = 255;
+ gled->mcdev.num_colors = 3;
subled_info = devm_kcalloc(&g15->hdev->dev, 3, sizeof(*subled_info), GFP_KERNEL);
if (!subled_info)
@@ -632,20 +938,20 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index)
switch (i + 1) {
case LED_COLOR_ID_RED:
subled_info[i].color_index = LED_COLOR_ID_RED;
- subled_info[i].intensity = g15->leds[index].red;
+ subled_info[i].intensity = gled->red;
break;
case LED_COLOR_ID_GREEN:
subled_info[i].color_index = LED_COLOR_ID_GREEN;
- subled_info[i].intensity = g15->leds[index].green;
+ subled_info[i].intensity = gled->green;
break;
case LED_COLOR_ID_BLUE:
subled_info[i].color_index = LED_COLOR_ID_BLUE;
- subled_info[i].intensity = g15->leds[index].blue;
+ subled_info[i].intensity = gled->blue;
break;
}
subled_info[i].channel = i;
}
- g15->leds[index].mcdev.subled_info = subled_info;
+ gled->mcdev.subled_info = subled_info;
}
static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
@@ -656,6 +962,23 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
g15->leds[i].cdev.name = name;
switch (g15->model) {
+ case LG_G13:
+ if (i < LG_G15_BRIGHTNESS_MAX) {
+ /* RGB backlight. */
+ lg_g15_setup_led_rgb(g15, i);
+ ret = devm_led_classdev_multicolor_register_ext(&g15->hdev->dev,
+ &g15->leds[i].mcdev,
+ NULL);
+ } else {
+ /* Macro keys */
+ g15->leds[i].cdev.brightness_set_blocking = lg_g13_mkey_led_set;
+ g15->leds[i].cdev.brightness_get = lg_g13_mkey_led_get;
+ g15->leds[i].cdev.max_brightness = 1;
+
+ ret = devm_led_classdev_register(&g15->hdev->dev,
+ &g15->leds[i].cdev);
+ }
+ break;
case LG_G15:
case LG_G15_V2:
g15->leds[i].cdev.brightness_get = lg_g15_led_get;
@@ -702,27 +1025,60 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
}
/* Common input device init code shared between keyboards and Z-10 speaker handling */
-static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,
- const char *name)
+static void lg_g15_init_input_dev_core(struct hid_device *hdev,
+ struct input_dev *input,
+ char const *name)
{
- int i;
-
- input->name = name;
- input->phys = hdev->phys;
- input->uniq = hdev->uniq;
+ input->name = name;
+ input->phys = hdev->phys;
+ input->uniq = hdev->uniq;
input->id.bustype = hdev->bus;
input->id.vendor = hdev->vendor;
input->id.product = hdev->product;
input->id.version = hdev->version;
input->dev.parent = &hdev->dev;
- input->open = lg_g15_input_open;
- input->close = lg_g15_input_close;
+ input->open = lg_g15_input_open;
+ input->close = lg_g15_input_close;
+}
+
+static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,
+ const char *name)
+{
+ int i;
+
+ lg_g15_init_input_dev_core(hdev, input, name);
/* Keys below the LCD, intended for controlling a menu on the LCD */
for (i = 0; i < 5; i++)
input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);
}
+static void lg_g13_init_input_dev(struct hid_device *hdev,
+ struct input_dev *input, const char *name,
+ struct input_dev *input_js, const char *name_js)
+{
+ /* Macropad. */
+ lg_g15_init_input_dev_core(hdev, input, name);
+ for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) {
+ if (g13_keys_for_bits[i]) {
+ input_set_capability(input, EV_KEY, g13_keys_for_bits[i]);
+ }
+ }
+
+ /* OBTW, we're a joystick, too... */
+ lg_g15_init_input_dev_core(hdev, input_js, name_js);
+ for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i) {
+ if (g13_keys_for_bits_js[i]) {
+ input_set_capability(input_js, EV_KEY, g13_keys_for_bits_js[i]);
+ }
+ }
+
+ input_set_capability(input_js, EV_ABS, ABS_X);
+ input_set_abs_params(input_js, ABS_X, 0, 255, 0, 0);
+ input_set_capability(input_js, EV_ABS, ABS_Y);
+ input_set_abs_params(input_js, ABS_Y, 0, 255, 0, 0);
+}
+
static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
static const char * const led_names[] = {
@@ -739,7 +1095,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
unsigned int connect_mask = 0;
bool has_ff000000 = false;
struct lg_g15_data *g15;
- struct input_dev *input;
+ struct input_dev *input, *input_js;
struct hid_report *rep;
int ret, i, gkeys = 0;
@@ -778,6 +1134,21 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_set_drvdata(hdev, (void *)g15);
switch (g15->model) {
+ case LG_G13:
+ /*
+ * Some usermode libraries tend to ignore devices that don't
+ * "look like" a joystick. Create additional input device
+ * dedicated as joystick.
+ */
+ input_js = devm_input_allocate_device(&hdev->dev);
+ if (!input_js)
+ return -ENOMEM;
+ g15->input_js = input_js;
+ input_set_drvdata(input_js, hdev);
+
+ connect_mask = HID_CONNECT_HIDRAW;
+ gkeys = 25;
+ break;
case LG_G15:
INIT_WORK(&g15->work, lg_g15_leds_changed_work);
/*
@@ -859,6 +1230,34 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto error_hw_stop;
return 0; /* All done */
+ } else if (g15->model == LG_G13) {
+ static char const * const g13_led_names[] = {
+ /* Backlight is shared between LCD and keys. */
+ "g13:rgb:kbd_backlight",
+ NULL, // Keep in sync with led_type enum
+ "g13:red:macro_preset_1",
+ "g13:red:macro_preset_2",
+ "g13:red:macro_preset_3",
+ "g13:red:macro_record",
+ };
+ lg_g13_init_input_dev(hdev,
+ input, "Logitech G13 Gaming Keypad",
+ input_js, "Logitech G13 Thumbstick");
+ ret = input_register_device(input);
+ if (ret)
+ goto error_hw_stop;
+ ret = input_register_device(input_js);
+ if (ret)
+ goto error_hw_stop;
+
+ for (i = 0; i < ARRAY_SIZE(g13_led_names); ++i) {
+ if (g13_led_names[i]) {
+ ret = lg_g15_register_led(g15, i, g13_led_names[i]);
+ if (ret)
+ goto error_hw_stop;
+ }
+ }
+ return 0;
}
/* Setup and register input device */
@@ -903,6 +1302,13 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
static const struct hid_device_id lg_g15_devices[] = {
+ /*
+ * The G13 is a macropad-only device with an LCD, LED backlighing,
+ * and joystick.
+ */
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_G13),
+ .driver_data = LG_G13 },
/* The G11 is a G15 without the LCD, treat it as a G15 */
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G11),
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND] HID: asus: fix UAF via HID_CLAIMED_INPUT validation
From: Qasim Ijaz @ 2025-08-10 18:10 UTC (permalink / raw)
To: jikos, bentiss, linux-input, linux-kernel; +Cc: stable
After hid_hw_start() is called hidinput_connect() will eventually be
called to set up the device with the input layer since the
HID_CONNECT_DEFAULT connect mask is used. During hidinput_connect()
all input and output reports are processed and corresponding hid_inputs
are allocated and configured via hidinput_configure_usages(). This
process involves slot tagging report fields and configuring usages
by setting relevant bits in the capability bitmaps. However it is possible
that the capability bitmaps are not set at all leading to the subsequent
hidinput_has_been_populated() check to fail leading to the freeing of the
hid_input and the underlying input device.
This becomes problematic because a malicious HID device like a
ASUS ROG N-Key keyboard can trigger the above scenario via a
specially crafted descriptor which then leads to a user-after-free
when the name of the freed input device is written to later on after
hid_hw_start(). Below, report 93 intentionally utilises the
HID_UP_UNDEFINED Usage Page which is skipped during usage
configuration, leading to the frees.
0x05, 0x0D, // Usage Page (Digitizer)
0x09, 0x05, // Usage (Touch Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x0D, // Report ID (13)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0xC5, // Usage (0xC5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs)
0x85, 0x5D, // Report ID (93)
0x06, 0x00, 0x00, // Usage Page (Undefined)
0x09, 0x01, // Usage (0x01)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x1B, // Report Count (27)
0x81, 0x02, // Input (Data,Var,Abs)
0xC0, // End Collection
Below is the KASAN splat after triggering the UAF:
[ 21.672709] ==================================================================
[ 21.673700] BUG: KASAN: slab-use-after-free in asus_probe+0xeeb/0xf80
[ 21.673700] Write of size 8 at addr ffff88810a0ac000 by task kworker/1:2/54
[ 21.673700]
[ 21.673700] CPU: 1 UID: 0 PID: 54 Comm: kworker/1:2 Not tainted 6.16.0-rc4-g9773391cf4dd-dirty #36 PREEMPT(voluntary)
[ 21.673700] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
[ 21.673700] Call Trace:
[ 21.673700] <TASK>
[ 21.673700] dump_stack_lvl+0x5f/0x80
[ 21.673700] print_report+0xd1/0x660
[ 21.673700] kasan_report+0xe5/0x120
[ 21.673700] __asan_report_store8_noabort+0x1b/0x30
[ 21.673700] asus_probe+0xeeb/0xf80
[ 21.673700] hid_device_probe+0x2ee/0x700
[ 21.673700] really_probe+0x1c6/0x6b0
[ 21.673700] __driver_probe_device+0x24f/0x310
[ 21.673700] driver_probe_device+0x4e/0x220
[...]
[ 21.673700]
[ 21.673700] Allocated by task 54:
[ 21.673700] kasan_save_stack+0x3d/0x60
[ 21.673700] kasan_save_track+0x18/0x40
[ 21.673700] kasan_save_alloc_info+0x3b/0x50
[ 21.673700] __kasan_kmalloc+0x9c/0xa0
[ 21.673700] __kmalloc_cache_noprof+0x139/0x340
[ 21.673700] input_allocate_device+0x44/0x370
[ 21.673700] hidinput_connect+0xcb6/0x2630
[ 21.673700] hid_connect+0xf74/0x1d60
[ 21.673700] hid_hw_start+0x8c/0x110
[ 21.673700] asus_probe+0x5a3/0xf80
[ 21.673700] hid_device_probe+0x2ee/0x700
[ 21.673700] really_probe+0x1c6/0x6b0
[ 21.673700] __driver_probe_device+0x24f/0x310
[ 21.673700] driver_probe_device+0x4e/0x220
[...]
[ 21.673700]
[ 21.673700] Freed by task 54:
[ 21.673700] kasan_save_stack+0x3d/0x60
[ 21.673700] kasan_save_track+0x18/0x40
[ 21.673700] kasan_save_free_info+0x3f/0x60
[ 21.673700] __kasan_slab_free+0x3c/0x50
[ 21.673700] kfree+0xcf/0x350
[ 21.673700] input_dev_release+0xab/0xd0
[ 21.673700] device_release+0x9f/0x220
[ 21.673700] kobject_put+0x12b/0x220
[ 21.673700] put_device+0x12/0x20
[ 21.673700] input_free_device+0x4c/0xb0
[ 21.673700] hidinput_connect+0x1862/0x2630
[ 21.673700] hid_connect+0xf74/0x1d60
[ 21.673700] hid_hw_start+0x8c/0x110
[ 21.673700] asus_probe+0x5a3/0xf80
[ 21.673700] hid_device_probe+0x2ee/0x700
[ 21.673700] really_probe+0x1c6/0x6b0
[ 21.673700] __driver_probe_device+0x24f/0x310
[ 21.673700] driver_probe_device+0x4e/0x220
[...]
Fixes: 9ce12d8be12c ("HID: asus: Add i2c touchpad support")
Cc: stable@vger.kernel.org
Signed-off-by: Qasim Ijaz <qasdev00@gmail.com>
---
drivers/hid/hid-asus.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 4b45e31f0bab..9bce9c84ab20 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1212,8 +1212,14 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_err(hdev, "Asus hw start failed: %d\n", ret);
return ret;
}
-
- if (!drvdata->input) {
+
+ /*
+ * Check that input registration succeeded. Checking that
+ * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
+ * were freed during registration due to no usages being mapped,
+ * leaving drvdata->input pointing to freed memory.
+ */
+ if (!drvdata->input || !(hdev->claimed & HID_CLAIMED_INPUT)) {
hid_err(hdev, "Asus input not registered\n");
ret = -ENOMEM;
goto err_stop_hw;
--
2.39.5
^ permalink raw reply related
* [PATCH v2 RESEND] HID: multitouch: fix slab out-of-bounds access in mt_report_fixup()
From: Qasim Ijaz @ 2025-08-10 18:09 UTC (permalink / raw)
To: jikos, bentiss, linux-input, linux-kernel; +Cc: stable, Jiri Slaby
A malicious HID device can trigger a slab out-of-bounds during
mt_report_fixup() by passing in report descriptor smaller than
607 bytes. mt_report_fixup() attempts to patch byte offset 607
of the descriptor with 0x25 by first checking if byte offset
607 is 0x15 however it lacks bounds checks to verify if the
descriptor is big enough before conducting this check. Fix
this bug by ensuring the descriptor size is at least 608
bytes before accessing it.
Below is the KASAN splat after the out of bounds access happens:
[ 13.671954] ==================================================================
[ 13.672667] BUG: KASAN: slab-out-of-bounds in mt_report_fixup+0x103/0x110
[ 13.673297] Read of size 1 at addr ffff888103df39df by task kworker/0:1/10
[ 13.673297]
[ 13.673297] CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Not tainted 6.15.0-00005-gec5d573d83f4-dirty #3
[ 13.673297] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/04
[ 13.673297] Call Trace:
[ 13.673297] <TASK>
[ 13.673297] dump_stack_lvl+0x5f/0x80
[ 13.673297] print_report+0xd1/0x660
[ 13.673297] kasan_report+0xe5/0x120
[ 13.673297] __asan_report_load1_noabort+0x18/0x20
[ 13.673297] mt_report_fixup+0x103/0x110
[ 13.673297] hid_open_report+0x1ef/0x810
[ 13.673297] mt_probe+0x422/0x960
[ 13.673297] hid_device_probe+0x2e2/0x6f0
[ 13.673297] really_probe+0x1c6/0x6b0
[ 13.673297] __driver_probe_device+0x24f/0x310
[ 13.673297] driver_probe_device+0x4e/0x220
[ 13.673297] __device_attach_driver+0x169/0x320
[ 13.673297] bus_for_each_drv+0x11d/0x1b0
[ 13.673297] __device_attach+0x1b8/0x3e0
[ 13.673297] device_initial_probe+0x12/0x20
[ 13.673297] bus_probe_device+0x13d/0x180
[ 13.673297] device_add+0xe3a/0x1670
[ 13.673297] hid_add_device+0x31d/0xa40
[...]
Fixes: c8000deb6836 ("HID: multitouch: Add support for GT7868Q")
Cc: stable@vger.kernel.org
Signed-off-by: Qasim Ijaz <qasdev00@gmail.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
---
v2:
- Simplify fix with a if-size check after discussion with Jiri Slaby
- Change explanation of bug to reflect inclusion of a if-size check
drivers/hid/hid-multitouch.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 294516a8f541..22c6314a8843 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1503,6 +1503,14 @@ static const __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (hdev->vendor == I2C_VENDOR_ID_GOODIX &&
(hdev->product == I2C_DEVICE_ID_GOODIX_01E8 ||
hdev->product == I2C_DEVICE_ID_GOODIX_01E9)) {
+ if (*size < 608) {
+ dev_info(
+ &hdev->dev,
+ "GT7868Q fixup: report descriptor is only %u bytes, skipping\n",
+ *size);
+ return rdesc;
+ }
+
if (rdesc[607] == 0x15) {
rdesc[607] = 0x25;
dev_info(
--
2.39.5
^ permalink raw reply related
* Re: [PATCH] HID: multitouch: fix integer overflow in set_abs()
From: Qasim Ijaz @ 2025-08-10 17:31 UTC (permalink / raw)
To: Jiri Slaby; +Cc: jikos, bentiss, linux-input, linux-kernel, stable
In-Reply-To: <f7257221-cbfa-4f51-8ac4-38060bfaf2f4@kernel.org>
On Thu, Jul 31, 2025 at 09:43:38AM +0200, Jiri Slaby wrote:
> On 24. 07. 25, 17:56, Qasim Ijaz wrote:
> > On Thu, Jul 24, 2025 at 08:58:40AM +0200, Jiri Slaby wrote:
> > > On 23. 07. 25, 19:36, Qasim Ijaz wrote:
> > > > It is possible for a malicious HID device to trigger a signed integer
> > > > overflow (undefined behaviour) in set_abs() in the following expression
> > > > by supplying bogus logical maximum and minimum values:
> > > >
> > > > int fuzz = snratio ? (fmax - fmin) / snratio : 0;
> > > >
> > > > For example, if the logical_maximum is INT_MAX and logical_minimum is -1
> > > > then (fmax - fmin) resolves to INT_MAX + 1, which does not fit in a 32-bit
> > > > signed int, so the subtraction overflows.
> > >
> > > The question is if it matters with -fwrapv?
> >
> > Ah yea thanks for bringing this up Jiri. I think you might be correct,
> > after doing some research it looks like the kernel enables -fno‑strict‑overflow
> > which implies -fwrapv which leads to wrap around instead of UB If I undestand
> > correctly. So with that in mind this patch probably doesn't do anything
> > useful, do you agree?
>
> Yes, it correctly wraps around. But the question remains :). Does it matter
> or not?
>
probably not. From what I can tell it doesn't look like any further security
issues occur as a result of the wrap around behaviour so i think its
probably best to drop this patch.
thanks,
qasim
> thanks,
> --
> js
> suse labs
^ permalink raw reply
* Re: [PATCH 05/21] x86/platform: select legacy gpiolib interfaces where used
From: Hans de Goede @ 2025-08-10 15:12 UTC (permalink / raw)
To: Arnd Bergmann, Andy Shevchenko, Arnd Bergmann
Cc: Bartosz Golaszewski, Linus Walleij, open list:GPIO SUBSYSTEM,
Dmitry Torokhov, Ilpo Järvinen, Lee Jones, Dzmitry Sankouski,
Heiko Stübner, linux, Thomas Weißschuh, Guenter Roeck,
linux-input, linux-kernel, platform-driver-x86
In-Reply-To: <3190334c-538d-4e2d-80a4-6e24b255e844@app.fastmail.com>
[-- Attachment #1: Type: text/plain, Size: 3900 bytes --]
Hi Arnd, Andy,
On 9-Aug-25 9:44 PM, Arnd Bergmann wrote:
> On Sat, Aug 9, 2025, at 12:00, Andy Shevchenko wrote:
>> On Fri, Aug 08, 2025 at 05:17:49PM +0200, Arnd Bergmann wrote:
>>> From: Arnd Bergmann <arnd@arndb.de>
>>>
>>> A few old machines have not been converted away from the old-style
>>> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
>>> symbol so the code still works where it is needed but can be left
>>> out otherwise.
>>
>>> --- a/drivers/platform/x86/x86-android-tablets/Kconfig
>>> +++ b/drivers/platform/x86/x86-android-tablets/Kconfig
>>> @@ -8,6 +8,7 @@ config X86_ANDROID_TABLETS
>>> depends on I2C && SPI && SERIAL_DEV_BUS
>>> depends on GPIOLIB && PMIC_OPREGION
>>> depends on ACPI && EFI && PCI
>>> + select GPIOLIB_LEGACY
>>> select NEW_LEDS
>>> select LEDS_CLASS
>>> select POWER_SUPPLY
>>
>> Hmm... This is a surprising change. But I leave it to Hans.
Yes I was surprised by this myself since I explicitly removed
all legacy GPIO use from the x86-android-tablets code a while
ago (or so I thought).
> I think the only function that still needs it is
> x86_android_tablet_probe() doing
>
> static struct gpio_keys_button *buttons;
>
> for (i = 0; i < dev_info->gpio_button_count; i++) {
> ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
> dev_info->gpio_button[i].pin,
> dev_info->gpio_button[i].button.desc,
> false, GPIOD_IN, &gpiod);
>
> buttons[i] = dev_info->gpio_button[i].button;
> buttons[i].gpio = desc_to_gpio(gpiod);
> /* Release GPIO descriptor so that gpio-keys can request it */
> devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
> }
>
> So the driver itself uses gpio descriptors, but it passes
> some of them into another driver by number. There is probably
> an easy workaround that I did not see.
Ah I see, so this is basically in the same boat as
drivers/input/misc/soc_button_array.c which also first
gets a gpio_desc and then calls desc_to_gpio() to store
the GPIO number in struct gpio_keys_button which is passed
as platform_data to drivers/input/keyboard/gpio_keys.c
The gpio_keys driver then converts things back
into a gpio_desc in gpio_keys_setup_key()
using devm_gpio_request_one() + gpio_to_desc()
So it looks like we need to add a gpiod member to
struct gpio_keys_button (include/linux/gpio_keys.h)
and modify gpio_keys.c to prefer that over using
button->gpio, something like the attached patch
basically.
I won't have time to work on this until September,
so if someone wants to take the attached patch and run
with it go for it.
Note the x86-android-tablets / soc_button_array code
will become responsible for requesting / releasing
the gpiod when using the new gpio_keys_button.gpiod
member.
For the x86-android-tablets code this is easy, just drop
these 2 lines:
/* Release GPIO descriptor so that gpio-keys can request it */
devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
And for soc_button_array.c it is _probably_ just a matter
of switching to devm_gpiod_get_index() and drop the
gpiod_put().
I have hardware to test both the x86-android-tablets
code as well as the soc_button_array code. I might be
able to do a quick test on August 22nd or 29th.
Regards,
Hans
p.s.
It looks like this will also help with:
drivers/platform/x86/meraki-mx100.c
drivers/platform/x86/pcengines-apuv2.c
drivers/platform/x86/barco-p50-gpio.c
I do not have hardware to test these, but if the changes
work for x86-android-tablets + soc_button_array then
hopefully they will be fine here too.
[-- Attachment #2: 0001-Input-gpio-keys-Allow-directly-passing-a-gpio_desc-i.patch --]
[-- Type: text/x-patch, Size: 2112 bytes --]
From 6fe4bd731db59bb01cc0ce3cbd2412434ea053c6 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hansg@kernel.org>
Date: Sun, 10 Aug 2025 16:58:01 +0200
Subject: [PATCH] Input: gpio-keys - Allow directly passing a gpio_desc instead
of a GPIO number
Using GPIO numbers is deprecated. Allow code passing GPIO-keys info through
platform-data (struct gpio_keys_button) to directly pass a gpio_desc
instead of a GPIO number.
Note requesting / releasing the gpio_desc will be the responsibility
of the code passing in the platform-data.
Signed-off-by: Hans de Goede <hansg@kernel.org>
---
drivers/input/keyboard/gpio_keys.c | 2 ++
include/linux/gpio_keys.h | 5 ++++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index f9db86da0818..323247f63a7e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -528,6 +528,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
*/
bdata->gpiod = NULL;
}
+ } else if (button->gpiod) {
+ bdata->gpiod = button->gpiod;
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number, so request the GPIO here and
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 80fa930b04c6..7c32c3a0695f 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -5,11 +5,13 @@
#include <linux/types.h>
struct device;
+struct gpio_desc;
/**
* struct gpio_keys_button - configuration parameters
* @code: input event code (KEY_*, SW_*)
- * @gpio: %-1 if this key does not support gpio
+ * @gpio: %-1 if this key does not support gpio (deprecated use gpiod)
+ * @gpiod: gpio_desc for the GPIO, NULL if this key does not support gpio
* @active_low: %true indicates that button is considered
* depressed when gpio is low
* @desc: label that will be attached to button's gpio
@@ -26,6 +28,7 @@ struct device;
struct gpio_keys_button {
unsigned int code;
int gpio;
+ struct gpio_desc *gpiod;
int active_low;
const char *desc;
unsigned int type;
--
2.49.0
^ permalink raw reply related
* Re: [PATCH 05/21] x86/platform: select legacy gpiolib interfaces where used
From: Arnd Bergmann @ 2025-08-09 19:44 UTC (permalink / raw)
To: Andy Shevchenko, Arnd Bergmann
Cc: Bartosz Golaszewski, Linus Walleij, open list:GPIO SUBSYSTEM,
Dmitry Torokhov, Hans de Goede, Ilpo Järvinen, Lee Jones,
Dzmitry Sankouski, Heiko Stübner, linux,
Thomas Weißschuh, Guenter Roeck, linux-input, linux-kernel,
platform-driver-x86
In-Reply-To: <aJccS7fdcx0INYTA@smile.fi.intel.com>
On Sat, Aug 9, 2025, at 12:00, Andy Shevchenko wrote:
> On Fri, Aug 08, 2025 at 05:17:49PM +0200, Arnd Bergmann wrote:
>> From: Arnd Bergmann <arnd@arndb.de>
>>
>> A few old machines have not been converted away from the old-style
>> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
>> symbol so the code still works where it is needed but can be left
>> out otherwise.
>
>> --- a/drivers/platform/x86/x86-android-tablets/Kconfig
>> +++ b/drivers/platform/x86/x86-android-tablets/Kconfig
>> @@ -8,6 +8,7 @@ config X86_ANDROID_TABLETS
>> depends on I2C && SPI && SERIAL_DEV_BUS
>> depends on GPIOLIB && PMIC_OPREGION
>> depends on ACPI && EFI && PCI
>> + select GPIOLIB_LEGACY
>> select NEW_LEDS
>> select LEDS_CLASS
>> select POWER_SUPPLY
>
> Hmm... This is a surprising change. But I leave it to Hans.
I think the only function that still needs it is
x86_android_tablet_probe() doing
static struct gpio_keys_button *buttons;
for (i = 0; i < dev_info->gpio_button_count; i++) {
ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
dev_info->gpio_button[i].pin,
dev_info->gpio_button[i].button.desc,
false, GPIOD_IN, &gpiod);
buttons[i] = dev_info->gpio_button[i].button;
buttons[i].gpio = desc_to_gpio(gpiod);
/* Release GPIO descriptor so that gpio-keys can request it */
devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
}
So the driver itself uses gpio descriptors, but it passes
some of them into another driver by number. There is probably
an easy workaround that I did not see.
Arnd
^ permalink raw reply
* Re: [PATCH 05/21] x86/platform: select legacy gpiolib interfaces where used
From: Andy Shevchenko @ 2025-08-09 10:00 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Bartosz Golaszewski, Linus Walleij, linux-gpio, Dmitry Torokhov,
Hans de Goede, Ilpo Järvinen, Arnd Bergmann, Lee Jones,
Dzmitry Sankouski, Heiko Stuebner, Dr. David Alan Gilbert,
Thomas Weißschuh, Guenter Roeck, linux-input, linux-kernel,
platform-driver-x86
In-Reply-To: <20250808151822.536879-6-arnd@kernel.org>
On Fri, Aug 08, 2025 at 05:17:49PM +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> A few old machines have not been converted away from the old-style
> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
> symbol so the code still works where it is needed but can be left
> out otherwise.
...
> --- a/drivers/platform/x86/x86-android-tablets/Kconfig
> +++ b/drivers/platform/x86/x86-android-tablets/Kconfig
> @@ -8,6 +8,7 @@ config X86_ANDROID_TABLETS
> depends on I2C && SPI && SERIAL_DEV_BUS
> depends on GPIOLIB && PMIC_OPREGION
> depends on ACPI && EFI && PCI
> + select GPIOLIB_LEGACY
> select NEW_LEDS
> select LEDS_CLASS
> select POWER_SUPPLY
Hmm... This is a surprising change. But I leave it to Hans.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH 09/21] input: gpio-keys: make legacy gpiolib optional
From: Arnd Bergmann @ 2025-08-08 15:17 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij, linux-gpio, Dmitry Torokhov,
Matti Vaittinen, Lee Jones
Cc: Arnd Bergmann, Gatien Chevallier, Fabrice Gasnier,
Andy Shevchenko, Bartosz Golaszewski, Thomas Gleixner,
Charles Keepax, Krzysztof Kozlowski, Christophe JAILLET,
linux-input, linux-kernel
In-Reply-To: <20250808151822.536879-1-arnd@kernel.org>
From: Arnd Bergmann <arnd@arndb.de>
Most users of gpio-keys and gpio-keys-polled use modern gpiolib
interfaces, but there are still number of ancient sh, arm32 and x86
machines that have never been converted.
Add an #ifdef block for the parts of the driver that are only
used on those legacy machines.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/input/keyboard/gpio_keys.c | 5 +++--
drivers/input/keyboard/gpio_keys_polled.c | 2 ++
drivers/mfd/rohm-bd71828.c | 2 ++
drivers/mfd/rohm-bd718x7.c | 2 ++
include/linux/gpio_keys.h | 2 ++
5 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index f9db86da0818..984b20f773ed 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -528,6 +528,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
*/
bdata->gpiod = NULL;
}
+#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number, so request the GPIO here and
@@ -546,6 +547,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
+#endif
}
if (bdata->gpiod) {
@@ -583,8 +585,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (irq < 0) {
error = irq;
dev_err_probe(dev, error,
- "Unable to get irq number for GPIO %d\n",
- button->gpio);
+ "Unable to get irq number for GPIO\n");
return error;
}
bdata->irq = irq;
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index e6707d72210e..0ae0e53910ea 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -301,6 +301,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(bdata->gpiod),
"failed to get gpio\n");
}
+#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number so request the GPIO here and
@@ -323,6 +324,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
+#endif
}
bdata->last_state = -1;
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index a14b7aa69c3c..fb68694fadca 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -21,7 +21,9 @@
static struct gpio_keys_button button = {
.code = KEY_POWER,
+#ifdef CONFIG_GPIOLIB_LEGACY
.gpio = -1,
+#endif
.type = EV_KEY,
};
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index 25e494a93d48..6c99ab62e31b 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -20,7 +20,9 @@
static struct gpio_keys_button button = {
.code = KEY_POWER,
+#ifdef CONFIG_GPIOLIB_LEGACY
.gpio = -1,
+#endif
.type = EV_KEY,
};
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 80fa930b04c6..e8d6dc290efb 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -25,7 +25,9 @@ struct device;
*/
struct gpio_keys_button {
unsigned int code;
+#ifdef CONFIG_GPIOLIB_LEGACY
int gpio;
+#endif
int active_low;
const char *desc;
unsigned int type;
--
2.39.5
^ permalink raw reply related
* [PATCH 05/21] x86/platform: select legacy gpiolib interfaces where used
From: Arnd Bergmann @ 2025-08-08 15:17 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij, linux-gpio, Dmitry Torokhov,
Hans de Goede, Ilpo Järvinen
Cc: Arnd Bergmann, Lee Jones, Dzmitry Sankouski, Heiko Stuebner,
Dr. David Alan Gilbert, Thomas Weißschuh, Guenter Roeck,
Andy Shevchenko, linux-input, linux-kernel, platform-driver-x86
In-Reply-To: <20250808151822.536879-1-arnd@kernel.org>
From: Arnd Bergmann <arnd@arndb.de>
A few old machines have not been converted away from the old-style
gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
symbol so the code still works where it is needed but can be left
out otherwise.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/input/misc/Kconfig | 3 +++
drivers/platform/x86/Kconfig | 3 +++
drivers/platform/x86/x86-android-tablets/Kconfig | 1 +
3 files changed, 7 insertions(+)
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 0fb21c99a5e3..681d4123ef2b 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -860,6 +860,9 @@ config INPUT_IDEAPAD_SLIDEBAR
config INPUT_SOC_BUTTON_ARRAY
tristate "Windows-compatible SoC Button Array"
depends on KEYBOARD_GPIO && ACPI
+ depends on X86
+ depends on GPIOLIB
+ select GPIOLIB_LEGACY
help
Say Y here if you have a SoC-based tablet that originally runs
Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later.
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 6d238e120dce..979a2fce8fc1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -309,6 +309,7 @@ config MERAKI_MX100
depends on GPIOLIB
depends on GPIO_ICH
depends on LEDS_CLASS
+ select GPIOLIB_LEGACY
select LEDS_GPIO
help
This driver provides support for the front button and LEDs on
@@ -564,6 +565,7 @@ config PCENGINES_APU2
depends on INPUT && INPUT_KEYBOARD && GPIOLIB
depends on LEDS_CLASS
select GPIO_AMD_FCH
+ select GPIOLIB_LEGACY
select KEYBOARD_GPIO_POLLED
select LEDS_GPIO
help
@@ -591,6 +593,7 @@ config PORTWELL_EC
config BARCO_P50_GPIO
tristate "Barco P50 GPIO driver for identify LED/button"
depends on GPIOLIB
+ select GPIOLIB_LEGACY
help
This driver provides access to the GPIOs for the identify button
and led present on Barco P50 board.
diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig
index 193da15ee01c..54fa6112c9ea 100644
--- a/drivers/platform/x86/x86-android-tablets/Kconfig
+++ b/drivers/platform/x86/x86-android-tablets/Kconfig
@@ -8,6 +8,7 @@ config X86_ANDROID_TABLETS
depends on I2C && SPI && SERIAL_DEV_BUS
depends on GPIOLIB && PMIC_OPREGION
depends on ACPI && EFI && PCI
+ select GPIOLIB_LEGACY
select NEW_LEDS
select LEDS_CLASS
select POWER_SUPPLY
--
2.39.5
^ permalink raw reply related
* [PATCH 00/21] gpiolib: fence off legacy interfaces
From: Arnd Bergmann @ 2025-08-08 15:17 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij, linux-gpio
Cc: Arnd Bergmann, Andrew Lunn, Sebastian Hesselbarth,
Gregory Clement, Russell King, Daniel Mack, Haojian Zhuang,
Robert Jarzmik, Krzysztof Kozlowski, Alim Akhtar,
Geert Uytterhoeven, Thomas Bogendoerfer, Yoshinori Sato,
Rich Felker, John Paul Adrian Glaubitz, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Dmitry Torokhov, Lee Jones, Pavel Machek, Mauro Carvalho Chehab,
Matti Vaittinen, Florian Fainelli, Jeff Johnson, Hans de Goede,
Ilpo Järvinen, Greg Kroah-Hartman, Jaroslav Kysela,
Takashi Iwai, Liam Girdwood, Mark Brown, Andy Shevchenko,
Dr. David Alan Gilbert, linux-arm-kernel, linux-kernel,
linux-samsung-soc, linux-m68k, linux-mips, linux-sh, linux-input,
linux-leds, linux-media, patches, netdev, linux-wireless, ath10k,
platform-driver-x86, linux-usb, linux-sound
From: Arnd Bergmann <arnd@arndb.de>
Commit 678bae2eaa81 ("gpiolib: make legacy interfaces optional") was
merged for linux-6.17, so now it is possible to use the legacy interfaces
conditionally and eventually have the support left out of the kernel
whenever it is not needed.
I created six patches to force-enable CONFIG_GPIOLIB_LEGACY on the
few (mostly ancient) platforms that still require this, plus a set of
patches to either add the corresponding Kconfig dependencies that make
the device drivers conditional on that symbol, or change them to no
longer require it.
The final patch ends up turning the Kconfig symbol off by default,
which of course depends on everything else getting merged first to avoid
build errors.
I would suggest that patches 1-20 can just get merged through the
respective maintainer trees independently when they are deemed ready,
and the final patch can wait another merge window.
Arnd
Arnd Bergmann (21):
ARM: select legacy gpiolib interfaces where used
m68k: coldfire: select legacy gpiolib interface for mcfqspi
mips: select legacy gpiolib interfaces where used
sh: select legacy gpiolib interface
x86/platform: select legacy gpiolib interfaces where used
x86/olpc: select GPIOLIB_LEGACY
mfd: wm8994: remove dead legacy-gpio code
ASoC: add GPIOLIB_LEGACY dependency where needed
input: gpio-keys: make legacy gpiolib optional
leds: gpio: make legacy gpiolib interface optional
media: em28xx: add special case for legacy gpiolib interface
mfd: arizona: make legacy gpiolib interface optional
mfd: si476x: add GPIOLIB_LEGACY dependency
mfd: aat2870: add GPIOLIB_LEGACY dependency
dsa: b53: hide legacy gpiolib usage on non-mips
ath10k: remove gpio number assignment
nfc: marvell: convert to gpio descriptors
nfc: s3fwrn5: convert to gpio descriptors
usb: udc: pxa: remove unused platform_data
ASoC: pxa: add GPIOLIB_LEGACY dependency
gpiolib: turn off legacy interface by default
arch/arm/mach-mv78xx0/Kconfig | 1 +
arch/arm/mach-orion5x/Kconfig | 1 +
arch/arm/mach-pxa/Kconfig | 1 +
arch/arm/mach-pxa/devices.c | 7 --
arch/arm/mach-pxa/gumstix.c | 1 -
arch/arm/mach-pxa/udc.h | 8 --
arch/arm/mach-s3c/Kconfig.s3c64xx | 1 +
arch/arm/mach-sa1100/Kconfig | 1 +
arch/m68k/Kconfig.cpu | 1 +
arch/mips/Kconfig | 5 +
arch/mips/alchemy/Kconfig | 1 -
arch/mips/txx9/Kconfig | 1 +
arch/sh/Kconfig | 1 +
arch/sh/boards/Kconfig | 8 ++
arch/sh/boards/mach-highlander/Kconfig | 1 +
arch/sh/boards/mach-rsk/Kconfig | 3 +
arch/x86/Kconfig | 1 +
drivers/gpio/Kconfig | 11 ++-
drivers/input/keyboard/gpio_keys.c | 5 +-
drivers/input/keyboard/gpio_keys_polled.c | 2 +
drivers/input/misc/Kconfig | 3 +
drivers/leds/leds-gpio.c | 8 +-
drivers/media/usb/em28xx/Kconfig | 1 +
drivers/media/usb/em28xx/em28xx-dvb.c | 4 +-
drivers/mfd/Kconfig | 2 +
drivers/mfd/arizona-irq.c | 5 +-
drivers/mfd/rohm-bd71828.c | 2 +
drivers/mfd/rohm-bd718x7.c | 2 +
drivers/mfd/wm8994-irq.c | 94 +------------------
drivers/net/dsa/b53/b53_common.c | 17 +---
drivers/net/dsa/b53/b53_priv.h | 24 +++--
drivers/net/wireless/ath/ath10k/leds.c | 3 +-
drivers/nfc/nfcmrvl/main.c | 47 +++-------
drivers/nfc/nfcmrvl/nfcmrvl.h | 5 +-
drivers/nfc/nfcmrvl/uart.c | 5 -
drivers/nfc/nfcmrvl/usb.c | 1 -
drivers/nfc/s3fwrn5/i2c.c | 42 +++------
drivers/nfc/s3fwrn5/phy_common.c | 12 +--
drivers/nfc/s3fwrn5/phy_common.h | 4 +-
drivers/nfc/s3fwrn5/uart.c | 30 ++----
drivers/platform/x86/Kconfig | 3 +
.../platform/x86/x86-android-tablets/Kconfig | 1 +
drivers/usb/gadget/udc/pxa25x_udc.c | 41 +++-----
drivers/usb/gadget/udc/pxa25x_udc.h | 2 +-
drivers/usb/gadget/udc/pxa27x_udc.c | 35 +------
drivers/usb/gadget/udc/pxa27x_udc.h | 2 -
include/linux/gpio_keys.h | 2 +
include/linux/leds.h | 2 +
include/linux/mfd/arizona/pdata.h | 6 ++
include/linux/mfd/wm8994/pdata.h | 5 -
include/linux/platform_data/pxa2xx_udc.h | 15 ---
sound/pci/Kconfig | 1 +
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/arizona-jack.c | 17 +++-
sound/soc/pxa/Kconfig | 4 +-
55 files changed, 192 insertions(+), 320 deletions(-)
delete mode 100644 arch/arm/mach-pxa/udc.h
--
2.39.5
Cc: Linus Walleij <linus.walleij@linaro.org> (maintainer:GPIO SUBSYSTEM,commit_signer:1/2=50%)
Cc: Bartosz Golaszewski <brgl@bgdev.pl> (maintainer:GPIO SUBSYSTEM,commit_signer:1/7=14%,commit_signer:1/2=50%)
Cc: linux-gpio@vger.kernel.org (open list:GPIO SUBSYSTEM)
Cc: Andrew Lunn <andrew@lunn.ch> (maintainer:ARM/Marvell Dove/MV78xx0/Orion SOC support)
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> (maintainer:ARM/Marvell Dove/MV78xx0/Orion SOC support)
Cc: Gregory Clement <gregory.clement@bootlin.com> (maintainer:ARM/Marvell Dove/MV78xx0/Orion SOC support)
Cc: Russell King <linux@armlinux.org.uk> (maintainer:ARM PORT)
Cc: Daniel Mack <daniel@zonque.org> (maintainer:PXA2xx/PXA3xx SUPPORT)
Cc: Haojian Zhuang <haojian.zhuang@gmail.com> (maintainer:PXA2xx/PXA3xx SUPPORT)
Cc: Robert Jarzmik <robert.jarzmik@free.fr> (maintainer:PXA2xx/PXA3xx SUPPORT)
Cc: Krzysztof Kozlowski <krzk@kernel.org> (maintainer:ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES,commit_signer:1/2=50%)
Cc: Alim Akhtar <alim.akhtar@samsung.com> (reviewer:ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES)
Cc: Geert Uytterhoeven <geert@linux-m68k.org> (maintainer:M68K ARCHITECTURE,commit_signer:1/4=25%,authored:1/4=25%,added_lines:2/13=15%,removed_lines:2/6=33%)
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de> (maintainer:MIPS)
Cc: Yoshinori Sato <ysato@users.sourceforge.jp> (maintainer:SUPERH)
Cc: Rich Felker <dalias@libc.org> (maintainer:SUPERH)
Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> (maintainer:SUPERH,commit_signer:2/4=50%)
Cc: Thomas Gleixner <tglx@linutronix.de> (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT),added_lines:4/36=11%,removed_lines:6/49=12%)
Cc: Ingo Molnar <mingo@redhat.com> (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT))
Cc: Borislav Petkov <bp@alien8.de> (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT))
Cc: Dave Hansen <dave.hansen@linux.intel.com> (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT))
Cc: x86@kernel.org (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT))
Cc: "H. Peter Anvin" <hpa@zytor.com> (reviewer:X86 ARCHITECTURE (32-BIT AND 64-BIT))
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> (maintainer:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,commit_signer:5/7=71%,authored:1/7=14%,added_lines:17/36=47%,removed_lines:27/49=55%,commit_signer:1/2=50%,commit_signer:3/5=60%)
Cc: Lee Jones <lee@kernel.org> (maintainer:LED SUBSYSTEM,commit_signer:2/5=40%)
Cc: Pavel Machek <pavel@kernel.org> (maintainer:LED SUBSYSTEM)
Cc: Mauro Carvalho Chehab <mchehab@kernel.org> (maintainer:EM28XX VIDEO4LINUX DRIVER)
Cc: Matti Vaittinen <mazziesaccount@gmail.com> (maintainer:ROHM POWER MANAGEMENT IC DEVICE DRIVERS)
Cc: Florian Fainelli <florian.fainelli@broadcom.com> (maintainer:BROADCOM B53/SF2 ETHERNET SWITCH DRIVER)
Cc: Jeff Johnson <jjohnson@kernel.org> (maintainer:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER)
Cc: Hans de Goede <hansg@kernel.org> (maintainer:X86 PLATFORM DRIVERS,commit_signer:1/7=14%)
Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> (maintainer:X86 PLATFORM DRIVERS)
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> (maintainer:USB SUBSYSTEM)
Cc: Jaroslav Kysela <perex@perex.cz> (maintainer:SOUND)
Cc: Takashi Iwai <tiwai@suse.com> (maintainer:SOUND,commit_signer:1/3=33%,authored:1/3=33%,removed_lines:2/2=100%)
Cc: Liam Girdwood <lgirdwood@gmail.com> (maintainer:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...)
Cc: Mark Brown <broonie@kernel.org> (maintainer:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,commit_signer:26/29=90%,commit_signer:1/3=33%)
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com> (authored:1/7=14%,added_lines:4/36=11%,removed_lines:6/49=12%,commit_signer:1/2=50%,authored:1/2=50%,added_lines:5/7=71%,removed_lines:7/7=100%,added_lines:7/7=100%,removed_lines:2/7=29%)
Cc: "Dr. David Alan Gilbert" <linux@treblig.org> (commit_signer:1/5=20%,authored:1/5=20%,removed_lines:7/10=70%)
Cc: linux-arm-kernel@lists.infradead.org (moderated list:ARM/Marvell Dove/MV78xx0/Orion SOC support)
Cc: linux-kernel@vger.kernel.org (open list)
Cc: linux-samsung-soc@vger.kernel.org (open list:ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES)
Cc: linux-m68k@lists.linux-m68k.org (open list:M68K ARCHITECTURE)
Cc: linux-mips@vger.kernel.org (open list:MIPS)
Cc: linux-sh@vger.kernel.org (open list:SUPERH)
Cc: linux-input@vger.kernel.org (open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...)
Cc: linux-leds@vger.kernel.org (open list:LED SUBSYSTEM)
Cc: linux-media@vger.kernel.org (open list:EM28XX VIDEO4LINUX DRIVER)
Cc: patches@opensource.cirrus.com (open list:WOLFSON MICROELECTRONICS DRIVERS)
Cc: netdev@vger.kernel.org (open list:BROADCOM B53/SF2 ETHERNET SWITCH DRIVER)
Cc: linux-wireless@vger.kernel.org (open list:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER)
Cc: ath10k@lists.infradead.org (open list:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER)
Cc: platform-driver-x86@vger.kernel.org (open list:X86 PLATFORM DRIVERS)
Cc: linux-usb@vger.kernel.org (open list:USB SUBSYSTEM)
Cc: linux-sound@vger.kernel.org (open list:SOUND)
^ permalink raw reply
* Re: [PATCH v9 1/6] dt-bindings: mfd: add pf1550
From: Samuel Kayode @ 2025-08-08 14:03 UTC (permalink / raw)
To: Sean Nyekjaer
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Girdwood, Mark Brown, Dmitry Torokhov, Sebastian Reichel,
Frank Li, imx, devicetree, linux-kernel, linux-input, linux-pm,
Abel Vesa, Abel Vesa, Robin Gong, Robin Gong,
Enric Balletbo i Serra, Christophe JAILLET, Krzysztof Kozlowski
In-Reply-To: <gj565636v5qgohhf5usklfqydkc6lzifzhrbquoyawbwvhdlma@kajszdivkp2e>
On Fri, Aug 08, 2025 at 10:10:35AM +0000, Sean Nyekjaer wrote:
> Does it make sense to show that the driver support suspend to mem
> states? Like...
I don't see any reason not to. So, I can add that in the next version.
>
>
> sw1_reg: sw1 {
> regulator-name = "sw1";
> regulator-min-microvolt = <1325000>;
> regulator-max-microvolt = <1325000>;
> regulator-always-on;
>
> regulator-state-mem {
> regulator-on-in-suspend;
> regulator-suspend-max-microvolt = <900000>;
> regulator-suspend-min-microvolt = <900000>;
> };
> };
>
> sw2_reg: sw2 {
> regulator-name = "sw2";
> regulator-min-microvolt = <1350000>;
> regulator-max-microvolt = <1350000>;
> regulator-always-on;
>
> regulator-state-mem {
> regulator-on-in-suspend;
> };
> };
>
> sw3_reg: sw3 {
> regulator-name = "sw3";
> regulator-min-microvolt = <3300000>;
> regulator-max-microvolt = <3300000>;
> regulator-always-on;
>
> regulator-state-mem {
> regulator-on-in-suspend;
> };
> };
>
> vldo1_reg: ldo1 {
> regulator-name = "ldo1";
> regulator-min-microvolt = <2900000>;
> regulator-max-microvolt = <2900000>;
> regulator-always-on;
>
> regulator-state-mem {
> regulator-off-in-suspend;
> };
> };
Will include these in the next version.
Thanks,
Sam
^ permalink raw reply
* Re: [PATCH v9 0/6] add support for pf1550 PMIC MFD-based drivers
From: Samuel Kayode @ 2025-08-08 13:46 UTC (permalink / raw)
To: Sean Nyekjaer
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Girdwood, Mark Brown, Dmitry Torokhov, Sebastian Reichel,
Frank Li, imx, devicetree, linux-kernel, linux-input, linux-pm,
Abel Vesa, Abel Vesa, Robin Gong, Robin Gong,
Enric Balletbo i Serra, Christophe JAILLET, Krzysztof Kozlowski
In-Reply-To: <znrv5235mga6ns4oue63o2acwmj5gge4c2mr32m7pui4lkamji@cu7zk4skmqkg>
On Fri, Aug 08, 2025 at 10:12:09AM +0000, Sean Nyekjaer wrote:
> On Wed, Jul 16, 2025 at 11:11:43AM +0100, Samuel Kayode via B4 Relay wrote:
> > This series adds support for pf1550 PMIC. It provides the core driver and a
> > three sub-drivers for the regulator, power supply and input subsystems.
> >
> > Patch 1 adds the DT binding document for the PMIC. Patches 2-5 adds the
> > pertinent drivers. Last patch adds a MAINTAINERS entry for the drivers.
> >
> > The patches 3-5 depend on the core driver provided in patch 2.
> >
>
> Please add to the whole series :)
>
> Tested-by: Sean Nyekjaer <sean@geanix.com>
>
Will do.
Thanks again for testing!
Thanks,
Sam
^ permalink raw reply
* Re: [PATCH v9 0/6] add support for pf1550 PMIC MFD-based drivers
From: Sean Nyekjaer @ 2025-08-08 10:12 UTC (permalink / raw)
To: samuel.kayode
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Girdwood, Mark Brown, Dmitry Torokhov, Sebastian Reichel,
Frank Li, imx, devicetree, linux-kernel, linux-input, linux-pm,
Abel Vesa, Abel Vesa, Robin Gong, Robin Gong,
Enric Balletbo i Serra, Christophe JAILLET, Krzysztof Kozlowski
In-Reply-To: <20250716-pf1550-v9-0-502a647f04ef@savoirfairelinux.com>
On Wed, Jul 16, 2025 at 11:11:43AM +0100, Samuel Kayode via B4 Relay wrote:
> This series adds support for pf1550 PMIC. It provides the core driver and a
> three sub-drivers for the regulator, power supply and input subsystems.
>
> Patch 1 adds the DT binding document for the PMIC. Patches 2-5 adds the
> pertinent drivers. Last patch adds a MAINTAINERS entry for the drivers.
>
> The patches 3-5 depend on the core driver provided in patch 2.
>
Please add to the whole series :)
Tested-by: Sean Nyekjaer <sean@geanix.com>
^ permalink raw reply
* Re: [PATCH v9 1/6] dt-bindings: mfd: add pf1550
From: Sean Nyekjaer @ 2025-08-08 10:10 UTC (permalink / raw)
To: samuel.kayode
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Girdwood, Mark Brown, Dmitry Torokhov, Sebastian Reichel,
Frank Li, imx, devicetree, linux-kernel, linux-input, linux-pm,
Abel Vesa, Abel Vesa, Robin Gong, Robin Gong,
Enric Balletbo i Serra, Christophe JAILLET, Krzysztof Kozlowski
In-Reply-To: <20250716-pf1550-v9-1-502a647f04ef@savoirfairelinux.com>
On Wed, Jul 16, 2025 at 11:11:44AM +0100, Samuel Kayode via B4 Relay wrote:
> From: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
>
> Add a DT binding document for pf1550 PMIC. This describes the core mfd
> device along with its children: regulators, charger and onkey.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> ---
> v9:
> - Add regulator suspend bindings in example
> - Add binding for disabling onkey power down
> - Fix thermal regulation temperature range
> v5:
> - Address Krzystof's feedback:
> - Drop monitored battery ref already included in power supply schema
> - Move `additionalProperties` close to `type` for regulator
> - Drop unneccessary |
> - Change `additionalProperties` to `unevaluatedProperties` for the
> PMIC
> v4:
> - Address Krzystof's feedback:
> - Filename changed to nxp,pf1550.yaml
> - Replace Freescale with NXP
> - Define include before battery-cell
> - Drop operating-range-celsius in example since
> nxp,thermal-regulation-celsisus already exists
> - Not sure if there is similar binding to thermal-regulation...
> for regulating temperature on thermal-zones? @Sebastian and @Krzysztof
> v3:
> - Address Krzysztof's feedback:
> - Fold charger and onkey objects
> - Drop compatible for sub-devices: onkey, charger and regulator.
> - Drop constant voltage property already included in
> monitored-battery
> - Fix whitespace warnings
> - Fix license
> v2:
> - Add yamls for the PMIC and the sub-devices
> ---
> .../devicetree/bindings/mfd/nxp,pf1550.yaml | 144 +++++++++++++++++++++
> 1 file changed, 144 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml b/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..ede5b6a2106ff60f4b47b3602fea8dd0b62d6fcf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
> @@ -0,0 +1,144 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/nxp,pf1550.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP PF1550 Power Management IC
> +
> +maintainers:
> + - Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> +
> +description:
> + PF1550 PMIC provides battery charging and power supply for low power IoT and
> + wearable applications. This device consists of an i2c controlled MFD that
> + includes regulators, battery charging and an onkey/power button.
> +
> +$ref: /schemas/power/supply/power-supply.yaml
> +
> +properties:
> + compatible:
> + const: nxp,pf1550
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + wakeup-source: true
> +
> + regulators:
> + type: object
> + additionalProperties: false
> +
> + patternProperties:
> + "^(ldo[1-3]|sw[1-3]|vrefddr)$":
> + type: object
> + $ref: /schemas/regulator/regulator.yaml
> + description:
> + regulator configuration for ldo1-3, buck converters(sw1-3)
> + and DDR termination reference voltage (vrefddr)
> + unevaluatedProperties: false
> +
> + monitored-battery:
> + description: |
> + A phandle to a monitored battery node that contains a valid value
> + for:
> + constant-charge-voltage-max-microvolt.
> +
> + nxp,thermal-regulation-celsius:
> + description:
> + Temperature threshold for thermal regulation of charger in celsius.
> + enum: [ 80, 95, 110, 125 ]
> +
> + nxp,min-system-microvolt:
> + description:
> + System specific lower limit voltage.
> + enum: [ 3500000, 3700000, 4300000 ]
> +
> + nxp,disable-key-power:
> + type: boolean
> + description:
> + Disable power-down using a long key-press. The onkey driver will remove
> + support for the KEY_POWER key press when triggered using a long press of
> + the onkey.
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/irq.h>
> + #include <dt-bindings/input/linux-event-codes.h>
> +
> + battery: battery-cell {
> + compatible = "simple-battery";
> + constant-charge-voltage-max-microvolt = <4400000>;
> + };
> +
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + pmic@8 {
> + compatible = "nxp,pf1550";
> + reg = <0x8>;
> +
> + interrupt-parent = <&gpio1>;
> + interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
> + wakeup-source;
> + monitored-battery = <&battery>;
> + nxp,min-system-microvolt = <4300000>;
> + nxp,thermal-regulation-celsius = <80>;
> +
> + regulators {
> + sw1_reg: sw1 {
> + regulator-name = "sw1";
> + regulator-min-microvolt = <600000>;
> + regulator-max-microvolt = <1387500>;
> + regulator-always-on;
> + regulator-ramp-delay = <6250>;
> + };
> +
> + sw2_reg: sw2 {
> + regulator-name = "sw2";
> + regulator-min-microvolt = <600000>;
> + regulator-max-microvolt = <1387500>;
> + regulator-always-on;
> + };
> +
> + sw3_reg: sw3 {
> + regulator-name = "sw3";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + };
> +
> + vldo1_reg: ldo1 {
> + regulator-name = "ldo1";
> + regulator-min-microvolt = <750000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + };
> +
> + vldo2_reg: ldo2 {
> + regulator-name = "ldo2";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + };
> +
> + vldo3_reg: ldo3 {
> + regulator-name = "ldo3";
> + regulator-min-microvolt = <750000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + };
> + };
> + };
> + };
>
> --
> 2.50.1
>
>
Does it make sense to show that the driver support suspend to mem
states? Like...
sw1_reg: sw1 {
regulator-name = "sw1";
regulator-min-microvolt = <1325000>;
regulator-max-microvolt = <1325000>;
regulator-always-on;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-max-microvolt = <900000>;
regulator-suspend-min-microvolt = <900000>;
};
};
sw2_reg: sw2 {
regulator-name = "sw2";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-state-mem {
regulator-on-in-suspend;
};
};
sw3_reg: sw3 {
regulator-name = "sw3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-state-mem {
regulator-on-in-suspend;
};
};
vldo1_reg: ldo1 {
regulator-name = "ldo1";
regulator-min-microvolt = <2900000>;
regulator-max-microvolt = <2900000>;
regulator-always-on;
regulator-state-mem {
regulator-off-in-suspend;
};
};
Br,
Sean
^ permalink raw reply
* [PATCH v3 RESEND 3/3] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
From: Rahul Rameshbabu @ 2025-08-08 6:13 UTC (permalink / raw)
To: linux-input, rust-for-linux, linux-kernel
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
In-Reply-To: <20250808061223.3770-1-sergeantsagara@protonmail.com>
Demonstrate how to perform a report fixup from a Rust HID driver. The mice
specify the const flag incorrectly in the consumer input report descriptor,
which leads to inputs being ignored. Correctly patch the report descriptor
for the Model O and O- mice.
Portions of the HID report post-fixup:
device 0:0
...
0x81, 0x06, // Input (Data,Var,Rel) 84
...
0x81, 0x06, // Input (Data,Var,Rel) 112
...
0x81, 0x06, // Input (Data,Var,Rel) 140
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
Notes:
Changelog:
v2->v3:
* Fixed docstring formatting
* Updated MAINTAINERS file based on v1 and v2 discussion
v1->v2:
* Use vendor id and device id from drivers/hid/hid-ids.h bindings
* Make use for dev_err! as appropriate
MAINTAINERS | 7 ++++
drivers/hid/Kconfig | 8 +++++
drivers/hid/Makefile | 1 +
drivers/hid/hid-glorious.c | 2 ++
drivers/hid/hid_glorious_rust.rs | 60 ++++++++++++++++++++++++++++++++
5 files changed, 78 insertions(+)
create mode 100644 drivers/hid/hid_glorious_rust.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c60765f2aaa..eee9a33914ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10200,6 +10200,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/gigabyte-wmi.c
+GLORIOUS RUST DRIVER [RUST]
+M: Rahul Rameshbabu <sergeantsagara@protonmail.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
+F: drivers/hid/hid_glorious_rust.rs
+
GNSS SUBSYSTEM
M: Johan Hovold <johan@kernel.org>
S: Maintained
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 922e76e18af2..b8ef750fb8b6 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -406,6 +406,14 @@ config HID_GLORIOUS
Support for Glorious PC Gaming Race mice such as
the Glorious Model O, O- and D.
+config HID_GLORIOUS_RUST
+ tristate "Glorious O and O- mice Rust reference driver"
+ depends on USB_HID
+ depends on RUST_HID_ABSTRACTIONS
+ help
+ Support for Glorious PC Gaming Race O and O- mice
+ in Rust.
+
config HID_HOLTEK
tristate "Holtek HID devices"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 10ae5dedbd84..bd86b3db5d88 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
+obj-$(CONFIG_HID_GLORIOUS_RUST) += hid_glorious_rust.o
obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
obj-$(CONFIG_HID_GOODIX_SPI) += hid-goodix-spi.o
obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o
diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c
index 5bbd81248053..d7362852c20f 100644
--- a/drivers/hid/hid-glorious.c
+++ b/drivers/hid/hid-glorious.c
@@ -76,8 +76,10 @@ static int glorious_probe(struct hid_device *hdev,
}
static const struct hid_device_id glorious_devices[] = {
+#if !IS_ENABLED(CONFIG_HID_GLORIOUS_RUST)
{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
USB_DEVICE_ID_GLORIOUS_MODEL_O) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
USB_DEVICE_ID_GLORIOUS_MODEL_D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LAVIEW,
diff --git a/drivers/hid/hid_glorious_rust.rs b/drivers/hid/hid_glorious_rust.rs
new file mode 100644
index 000000000000..8cffc1c605dd
--- /dev/null
+++ b/drivers/hid/hid_glorious_rust.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Rust reference HID driver for Glorious Model O and O- mice.
+
+use kernel::{self, bindings, device, hid, prelude::*};
+
+struct GloriousRust;
+
+kernel::hid_device_table!(
+ HID_TABLE,
+ MODULE_HID_TABLE,
+ <GloriousRust as hid::Driver>::IdInfo,
+ [(
+ hid::DeviceId::new_usb(
+ hid::Group::Generic,
+ bindings::USB_VENDOR_ID_SINOWEALTH,
+ bindings::USB_DEVICE_ID_GLORIOUS_MODEL_O,
+ ),
+ (),
+ )]
+);
+
+#[vtable]
+impl hid::Driver for GloriousRust {
+ type IdInfo = ();
+ const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+
+ /// Fix the Glorious Model O and O- consumer input report descriptor to use
+ /// the variable and relative flag, while clearing the const flag.
+ ///
+ /// Without this fixup, inputs from the mice will be ignored.
+ fn report_fixup<'a, 'b: 'a>(hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+ if rdesc.len() == 213
+ && (rdesc[84] == 129 && rdesc[85] == 3)
+ && (rdesc[112] == 129 && rdesc[113] == 3)
+ && (rdesc[140] == 129 && rdesc[141] == 3)
+ {
+ dev_info!(
+ hdev.as_ref(),
+ "patching Glorious Model O consumer control report descriptor\n"
+ );
+
+ rdesc[85] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ rdesc[113] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ rdesc[141] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ }
+
+ rdesc
+ }
+}
+
+kernel::module_hid_driver! {
+ type: GloriousRust,
+ name: "GloriousRust",
+ authors: ["Rahul Rameshbabu <sergeantsagara@protonmail.com>"],
+ description: "Rust reference HID driver for Glorious Model O and O- mice",
+ license: "GPL",
+}
--
2.47.2
^ permalink raw reply related
* [PATCH v3 RESEND 1/3] HID: core: Change hid_driver to use a const char* for name
From: Rahul Rameshbabu @ 2025-08-08 6:13 UTC (permalink / raw)
To: linux-input, rust-for-linux, linux-kernel
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
In-Reply-To: <20250808061223.3770-1-sergeantsagara@protonmail.com>
name is never mutated by the core HID stack. Making name a const char*
simplifies passing the string from Rust to C. Otherwise, it becomes
difficult to pass a 'static lifetime CStr from Rust to a char*, rather than
a const char*, due to lack of guarantee that the underlying data of the
CStr will not be mutated by the C code.
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
include/linux/hid.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 568a9d8c749b..d65c202783da 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -816,7 +816,7 @@ struct hid_usage_id {
* zero from them.
*/
struct hid_driver {
- char *name;
+ const char *name;
const struct hid_device_id *id_table;
struct list_head dyn_list;
--
2.47.2
^ permalink raw reply related
* [PATCH v3 RESEND 2/3] rust: core abstractions for HID drivers
From: Rahul Rameshbabu @ 2025-08-08 6:13 UTC (permalink / raw)
To: linux-input, rust-for-linux, linux-kernel
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
In-Reply-To: <20250808061223.3770-1-sergeantsagara@protonmail.com>
These abstractions enable the development of HID drivers in Rust by binding
with the HID core C API. They provide Rust types that map to the
equivalents in C. In this initial draft, only hid_device and hid_device_id
are provided direct Rust type equivalents. hid_driver is specially wrapped
with a custom Driver type. The module_hid_driver! macro provides analogous
functionality to its C equivalent. Only the .report_fixup callback is
binded to Rust so far.
Future work for these abstractions would include more bindings for common
HID-related types, such as hid_field, hid_report_enum, and hid_report as
well as more bus callbacks. Providing Rust equivalents to useful core HID
functions will also be necessary for HID driver development in Rust.
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
Notes:
Some points I did not address from the last review cycle:
* I did not look into autogenerating all the getter functions for various
fields exported from the binded C structures.
- I would be interested in hearing opinions from folks actively involved
with Rust for Linux on this topic.
Changelog:
v2->v3:
* Implemented AlwaysRefCounted trait using embedded struct device's
reference counts instead of the separate reference counter in struct
hid_device
* Used &raw mut as appropriate
* Binded include/linux/device.h for get_device and put_device
* Cleaned up various comment related formatting
* Minified dev_err! format string
* Updated Group enum to be repr(u16)
* Implemented From<u16> trait for Group
* Added TODO comment when const_trait_impl stabilizes
* Made group getter functions return a Group variant instead of a raw
number
* Made sure example code builds
v1->v2:
* Binded drivers/hid/hid-ids.h for use in Rust drivers
* Remove pre-emptive referencing of a C HID driver instance before
it is fully initialized in the driver registration path
* Moved static getters to generic Device trait implementation, so
they can be used by all device::DeviceContext
* Use core macros for supporting DeviceContext transitions
* Implemented the AlwaysRefCounted and AsRef traits
* Make use for dev_err! as appropriate
RFC->v1:
* Use Danilo's core infrastructure
* Account for HID device groups
* Remove probe and remove callbacks
* Implement report_fixup support
* Properly comment code including SAFETY comments
MAINTAINERS | 9 +
drivers/hid/Kconfig | 8 +
rust/bindings/bindings_helper.h | 3 +
rust/kernel/hid.rs | 503 ++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
5 files changed, 525 insertions(+)
create mode 100644 rust/kernel/hid.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index dd810da5261b..6c60765f2aaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10686,6 +10686,15 @@ F: include/uapi/linux/hid*
F: samples/hid/
F: tools/testing/selftests/hid/
+HID CORE LAYER [RUST]
+M: Rahul Rameshbabu <sergeantsagara@protonmail.com>
+R: Benjamin Tissoires <bentiss@kernel.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
+F: drivers/hid/*.rs
+F: rust/kernel/hid.rs
+
HID LOGITECH DRIVERS
R: Filipe Laíns <lains@riseup.net>
L: linux-input@vger.kernel.org
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 43859fc75747..922e76e18af2 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -744,6 +744,14 @@ config HID_MEGAWORLD_FF
Say Y here if you have a Mega World based game controller and want
to have force feedback support for it.
+config RUST_HID_ABSTRACTIONS
+ bool "Rust HID abstractions support"
+ depends on RUST
+ depends on HID=y
+ help
+ Adds support needed for HID drivers written in Rust. It provides a
+ wrapper around the C hid core.
+
config HID_REDRAGON
tristate "Redragon keyboards"
default !EXPERT
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 8cbb660e2ec2..7145fb1cdff1 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -45,6 +45,7 @@
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cred.h>
+#include <linux/device.h>
#include <linux/device/faux.h>
#include <linux/dma-mapping.h>
#include <linux/errname.h>
@@ -52,6 +53,8 @@
#include <linux/file.h>
#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/hid.h>
+#include "../../drivers/hid/hid-ids.h"
#include <linux/jiffies.h>
#include <linux/jump_label.h>
#include <linux/mdio.h>
diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
new file mode 100644
index 000000000000..a93804af8b78
--- /dev/null
+++ b/rust/kernel/hid.rs
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Abstractions for the HID interface.
+//!
+//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
+
+use crate::{device, device_id::RawDeviceId, driver, error::*, prelude::*, types::Opaque};
+use core::{
+ marker::PhantomData,
+ ptr::{addr_of_mut, NonNull},
+};
+
+/// Indicates the item is static read-only.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
+
+/// Indicates the item represents data from a physical control.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
+
+/// Indicates the item should be treated as a relative change from the previous
+/// report.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
+
+/// Indicates whether the control has a preferred state it will physically
+/// return to without user intervention.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
+
+/// Indicates whether the control has a physical state where it will not send
+/// any reports.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
+
+/// Indicates whether the control requires host system logic to change state.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
+
+/// Indicates whether the item is fixed size or a variable buffer of bytes.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
+
+/// HID device groups are intended to help categories HID devices based on a set
+/// of common quirks and logic that they will require to function correctly.
+#[repr(u16)]
+pub enum Group {
+ /// Used to match a device against any group when probing.
+ Any = bindings::HID_GROUP_ANY as u16,
+
+ /// Indicates a generic device that should need no custom logic from the
+ /// core HID stack.
+ Generic = bindings::HID_GROUP_GENERIC as u16,
+
+ /// Maps multitouch devices to hid-multitouch instead of hid-generic.
+ Multitouch = bindings::HID_GROUP_MULTITOUCH as u16,
+
+ /// Used for autodetecing and mapping of HID sensor hubs to
+ /// hid-sensor-hub.
+ SensorHub = bindings::HID_GROUP_SENSOR_HUB as u16,
+
+ /// Used for autodetecing and mapping Win 8 multitouch devices to set the
+ /// needed quirks.
+ MultitouchWin8 = bindings::HID_GROUP_MULTITOUCH_WIN_8 as u16,
+
+ // Vendor-specific device groups.
+ /// Used to distinguish Synpatics touchscreens from other products. The
+ /// touchscreens will be handled by hid-multitouch instead, while everything
+ /// else will be managed by hid-rmi.
+ RMI = bindings::HID_GROUP_RMI as u16,
+
+ /// Used for hid-core handling to automatically identify Wacom devices and
+ /// have them probed by hid-wacom.
+ Wacom = bindings::HID_GROUP_WACOM as u16,
+
+ /// Used by logitech-djreceiver and logitech-djdevice to autodetect if
+ /// devices paied to the DJ receivers are DJ devices and handle them with
+ /// the device driver.
+ LogitechDJDevice = bindings::HID_GROUP_LOGITECH_DJ_DEVICE as u16,
+
+ /// Since the Valve Steam Controller only has vendor-specific usages,
+ /// prevent hid-generic from parsing its reports since there would be
+ /// nothing hid-generic could do for the device.
+ Steam = bindings::HID_GROUP_STEAM as u16,
+
+ /// Used to differentiate 27 Mhz frequency Logitech DJ devices from other
+ /// Logitech DJ devices.
+ Logitech27MHzDevice = bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE as u16,
+
+ /// Used for autodetecting and mapping Vivaldi devices to hid-vivaldi.
+ Vivaldi = bindings::HID_GROUP_VIVALDI as u16,
+}
+
+// TODO: use `const_trait_impl` once stabilized:
+//
+// ```
+// impl const From<Group> for u16 {
+// /// [`Group`] variants are represented by [`u16`] values.
+// fn from(value: Group) -> Self {
+// value as Self
+// }
+// }
+// ```
+impl Group {
+ /// Internal function used to convert [`Group`] variants into [`u16`].
+ const fn into(self) -> u16 {
+ self as u16
+ }
+}
+
+impl From<u16> for Group {
+ /// [`u16`] values can be safely converted to [`Group`] variants.
+ fn from(value: u16) -> Self {
+ match value.into() {
+ bindings::HID_GROUP_GENERIC => Group::Generic,
+ bindings::HID_GROUP_MULTITOUCH => Group::Multitouch,
+ bindings::HID_GROUP_SENSOR_HUB => Group::SensorHub,
+ bindings::HID_GROUP_MULTITOUCH_WIN_8 => Group::MultitouchWin8,
+ bindings::HID_GROUP_RMI => Group::RMI,
+ bindings::HID_GROUP_WACOM => Group::Wacom,
+ bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Group::LogitechDJDevice,
+ bindings::HID_GROUP_STEAM => Group::Steam,
+ bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Group::Logitech27MHzDevice,
+ bindings::HID_GROUP_VIVALDI => Group::Vivaldi,
+ _ => Group::Any,
+ }
+ }
+}
+
+/// The HID device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct hid_device`.
+/// The implementation abstracts the usage of an already existing C `struct
+/// hid_device` within Rust code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct hid_device` created by the
+/// C portion of the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+ Opaque<bindings::hid_device>,
+ PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+ fn as_raw(&self) -> *mut bindings::hid_device {
+ self.0.get()
+ }
+
+ /// Returns the HID transport bus ID.
+ pub fn bus(&self) -> u16 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.bus
+ }
+
+ /// Returns the HID report group.
+ pub fn group(&self) -> Group {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.group.into()
+ }
+
+ /// Returns the HID vendor ID.
+ pub fn vendor(&self) -> u32 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.vendor
+ }
+
+ /// Returns the HID product ID.
+ pub fn product(&self) -> u32 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.product
+ }
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for Device {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+ unsafe { bindings::get_device(&raw mut (*self.as_raw()).dev) };
+ }
+
+ unsafe fn dec_ref(obj: NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+ unsafe { bindings::put_device(&raw mut (*obj.cast::<bindings::hid_device>().as_ptr()).dev) }
+ }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &device::Device<Ctx> {
+ // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+ // `struct hid_device`.
+ let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
+
+ // SAFETY: `dev` points to a valid `struct device`.
+ unsafe { device::Device::as_ref(dev) }
+ }
+}
+
+/// Abstraction for the HID device ID structure `struct hid_device_id`.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::hid_device_id);
+
+impl DeviceId {
+ /// Equivalent to C's `HID_USB_DEVICE` macro.
+ ///
+ /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
+ /// number.
+ pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
+ Self(bindings::hid_device_id {
+ bus: 0x3, // BUS_USB
+ group: group.into(),
+ vendor,
+ product,
+ driver_data: 0,
+ })
+ }
+
+ /// Returns the HID transport bus ID.
+ pub fn bus(&self) -> u16 {
+ self.0.bus
+ }
+
+ /// Returns the HID report group.
+ pub fn group(&self) -> Group {
+ self.0.group.into()
+ }
+
+ /// Returns the HID vendor ID.
+ pub fn vendor(&self) -> u32 {
+ self.0.vendor
+ }
+
+ /// Returns the HID product ID.
+ pub fn product(&self) -> u32 {
+ self.0.product
+ }
+}
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)` wrapper of `hid_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceId for DeviceId {
+ type RawType = bindings::hid_device_id;
+
+ const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::hid_device_id, driver_data);
+
+ fn index(&self) -> usize {
+ self.0.driver_data
+ }
+}
+
+/// [`IdTable`] type for HID.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a HID [`IdTable`] with its alias for modpost.
+#[macro_export]
+macro_rules! hid_device_table {
+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+ const $table_name: $crate::device_id::IdArray<
+ $crate::hid::DeviceId,
+ $id_info_type,
+ { $table_data.len() },
+ > = $crate::device_id::IdArray::new($table_data);
+
+ $crate::module_device_table!("hid", $module_table_name, $table_name);
+ };
+}
+
+/// The HID driver trait.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{bindings, device, hid};
+///
+/// struct MyDriver;
+///
+/// kernel::hid_device_table!(
+/// HID_TABLE,
+/// MODULE_HID_TABLE,
+/// <MyDriver as hid::Driver>::IdInfo,
+/// [(
+/// hid::DeviceId::new_usb(
+/// hid::Group::Steam,
+/// bindings::USB_VENDOR_ID_VALVE,
+/// bindings::USB_DEVICE_ID_STEAM_DECK,
+/// ),
+/// (),
+/// )]
+/// );
+///
+/// #[vtable]
+/// impl hid::Driver for MyDriver {
+/// type IdInfo = ();
+/// const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+///
+/// /// This function is optional to implement.
+/// fn report_fixup<'a, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+/// // Perform some report descriptor fixup.
+/// rdesc
+/// }
+/// }
+/// ```
+/// Drivers must implement this trait in order to get a HID driver registered.
+/// Please refer to the `Adapter` documentation for an example.
+#[vtable]
+pub trait Driver: Send {
+ /// The type holding information about each device id supported by the driver.
+ // TODO: Use `associated_type_defaults` once stabilized:
+ //
+ // ```
+ // type IdInfo: 'static = ();
+ // ```
+ type IdInfo: 'static;
+
+ /// The table of device ids supported by the driver.
+ const ID_TABLE: IdTable<Self::IdInfo>;
+
+ /// Called before report descriptor parsing. Can be used to mutate the
+ /// report descriptor before the core HID logic processes the descriptor.
+ /// Useful for problematic report descriptors that prevent HID devices from
+ /// functioning correctly.
+ ///
+ /// Optional to implement.
+ fn report_fixup<'a, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
+ build_error!(VTABLE_DEFAULT_ERROR)
+ }
+}
+
+/// An adapter for the registration of HID drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ type RegType = bindings::hid_driver;
+
+ unsafe fn register(
+ hdrv: &Opaque<Self::RegType>,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ // SAFETY: It's safe to set the fields of `struct hid_driver` on initialization.
+ unsafe {
+ (*hdrv.get()).name = name.as_char_ptr();
+ (*hdrv.get()).id_table = T::ID_TABLE.as_ptr();
+ (*hdrv.get()).report_fixup = if T::HAS_REPORT_FIXUP {
+ Some(Self::report_fixup_callback)
+ } else {
+ None
+ };
+ }
+
+ // SAFETY: `hdrv` is guaranteed to be a valid `RegType`
+ to_result(unsafe {
+ bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
+ })
+ }
+
+ unsafe fn unregister(hdrv: &Opaque<Self::RegType>) {
+ // SAFETY: `hdrv` is guaranteed to be a valid `RegType`
+ unsafe { bindings::hid_unregister_driver(hdrv.get()) }
+ }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+ extern "C" fn report_fixup_callback(
+ hdev: *mut bindings::hid_device,
+ buf: *mut u8,
+ size: *mut kernel::ffi::c_uint,
+ ) -> *const u8 {
+ // SAFETY: The HID subsystem only ever calls the report_fixup callback
+ // with a valid pointer to a `struct hid_device`.
+ //
+ // INVARIANT: `hdev` is valid for the duration of
+ // `report_fixup_callback()`.
+ let hdev = unsafe { &*hdev.cast::<Device<device::Core>>() };
+
+ // SAFETY: The HID subsystem only ever calls the report_fixup callback
+ // with a valid pointer to a `kernel::ffi::c_uint`.
+ //
+ // INVARIANT: `size` is valid for the duration of
+ // `report_fixup_callback()`.
+ let buf_len: usize = match unsafe { *size }.try_into() {
+ Ok(len) => len,
+ Err(e) => {
+ dev_err!(
+ hdev.as_ref(),
+ "Cannot fix report description due to {}!\n",
+ e
+ );
+
+ return buf;
+ }
+ };
+
+ // Build a mutable Rust slice from `buf` and `size`.
+ //
+ // SAFETY: The HID subsystem only ever calls the `report_fixup callback`
+ // with a valid pointer to a `u8` buffer.
+ //
+ // INVARIANT: `buf` is valid for the duration of
+ // `report_fixup_callback()`.
+ let rdesc_slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
+ let rdesc_slice = T::report_fixup(hdev, rdesc_slice);
+
+ match rdesc_slice.len().try_into() {
+ // SAFETY: The HID subsystem only ever calls the report_fixup
+ // callback with a valid pointer to a `kernel::ffi::c_uint`.
+ //
+ // INVARIANT: `size` is valid for the duration of
+ // `report_fixup_callback()`.
+ Ok(len) => unsafe { *size = len },
+ Err(e) => {
+ dev_err!(
+ hdev.as_ref(),
+ "Fixed report description will not be used due to {}!\n",
+ e
+ );
+
+ return buf;
+ }
+ }
+
+ rdesc_slice.as_ptr()
+ }
+}
+
+/// Declares a kernel module that exposes a single HID driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_hid_driver! {
+/// type: MyDriver,
+/// name: "Module name",
+/// authors: ["Author name"],
+/// description: "Description",
+/// license: "GPL",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_hid_driver {
+ ($($f:tt)*) => {
+ $crate::module_driver!(<T>, $crate::hid::Adapter<T>, { $($f)* });
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index e88bc4b27d6e..44c107f20174 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -80,6 +80,8 @@
pub mod firmware;
pub mod fmt;
pub mod fs;
+#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
+pub mod hid;
pub mod init;
pub mod io;
pub mod ioctl;
--
2.47.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox