* [PATCH v3 1/9] iio: hid-sensors: drop redundant iio_dev argument
From: Sanjay Chitroda @ 2026-05-09 10:10 UTC (permalink / raw)
To: jikos, jic23, srinivas.pandruvada
Cc: dlechner, nuno.sa, andy, sanjayembeddedse, sakari.ailus,
linux-input, linux-iio, linux-kernel
In-Reply-To: <20260509101040.791404-1-sanjayembedded@gmail.com>
From: Sanjay Chitroda <sanjayembeddedse@gmail.com>
hid_sensor_remove_trigger() uses struct hid_sensor_common to release
resources acquired during trigger setup.
Earlier implementations required struct iio_dev to clean up buffers,
but with the current code this argument is no longer used and is
redundant.
Adapt to hid_sensor_remove_trigger() API change across all HID IIO
drivers to match updated prototype.
Removing it simplifies the API and is a preparatory step toward
converting the trigger handling to a devm-based API.
Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
drivers/iio/accel/hid-sensor-accel-3d.c | 4 ++--
drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 3 +--
drivers/iio/common/hid-sensors/hid-sensor-trigger.h | 3 +--
drivers/iio/gyro/hid-sensor-gyro-3d.c | 4 ++--
drivers/iio/humidity/hid-sensor-humidity.c | 4 ++--
drivers/iio/light/hid-sensor-als.c | 4 ++--
drivers/iio/light/hid-sensor-prox.c | 4 ++--
drivers/iio/magnetometer/hid-sensor-magn-3d.c | 4 ++--
drivers/iio/orientation/hid-sensor-incl-3d.c | 4 ++--
drivers/iio/orientation/hid-sensor-rotation.c | 4 ++--
drivers/iio/position/hid-sensor-custom-intel-hinge.c | 4 ++--
drivers/iio/pressure/hid-sensor-press.c | 4 ++--
drivers/iio/temperature/hid-sensor-temperature.c | 4 ++--
13 files changed, 24 insertions(+), 26 deletions(-)
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 2ff591b3458f..a63dae90dadc 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -416,7 +416,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
+ hid_sensor_remove_trigger(&accel_state->common_attributes);
return ret;
}
@@ -429,7 +429,7 @@ static void hid_accel_3d_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
+ hid_sensor_remove_trigger(&accel_state->common_attributes);
}
static const struct platform_device_id hid_accel_3d_ids[] = {
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 417c4ab8c1b2..28d050b45c74 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -218,8 +218,7 @@ static const struct iio_buffer_setup_ops hid_sensor_buffer_ops = {
.predisable = buffer_predisable,
};
-void hid_sensor_remove_trigger(struct iio_dev *indio_dev,
- struct hid_sensor_common *attrb)
+void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
{
if (atomic_read(&attrb->runtime_pm_enable))
pm_runtime_disable(&attrb->pdev->dev);
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
index f94fca4f1edf..afec46ecbe71 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -16,8 +16,7 @@ extern const struct dev_pm_ops hid_sensor_pm_ops;
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
struct hid_sensor_common *attrb);
-void hid_sensor_remove_trigger(struct iio_dev *indio_dev,
- struct hid_sensor_common *attrb);
+void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);
int hid_sensor_power_state(struct hid_sensor_common *st, bool state);
#endif
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index c340cc899a7c..fe663b19e902 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -354,7 +354,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
+ hid_sensor_remove_trigger(&gyro_state->common_attributes);
return ret;
}
@@ -367,7 +367,7 @@ static void hid_gyro_3d_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
+ hid_sensor_remove_trigger(&gyro_state->common_attributes);
}
static const struct platform_device_id hid_gyro_3d_ids[] = {
diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c
index be2338d5f407..e580a2af9562 100644
--- a/drivers/iio/humidity/hid-sensor-humidity.c
+++ b/drivers/iio/humidity/hid-sensor-humidity.c
@@ -255,7 +255,7 @@ static int hid_humidity_probe(struct platform_device *pdev)
error_remove_callback:
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &humid_st->common_attributes);
+ hid_sensor_remove_trigger(&humid_st->common_attributes);
return ret;
}
@@ -268,7 +268,7 @@ static void hid_humidity_remove(struct platform_device *pdev)
iio_device_unregister(indio_dev);
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
- hid_sensor_remove_trigger(indio_dev, &humid_st->common_attributes);
+ hid_sensor_remove_trigger(&humid_st->common_attributes);
}
static const struct platform_device_id hid_humidity_ids[] = {
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 384572844162..9b57cdced18a 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -432,7 +432,7 @@ static int hid_als_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
+ hid_sensor_remove_trigger(&als_state->common_attributes);
return ret;
}
@@ -445,7 +445,7 @@ static void hid_als_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
+ hid_sensor_remove_trigger(&als_state->common_attributes);
}
static const struct platform_device_id hid_als_ids[] = {
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index efa904a70d0e..473c45626487 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -340,7 +340,7 @@ static int hid_prox_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
+ hid_sensor_remove_trigger(&prox_state->common_attributes);
return ret;
}
@@ -353,7 +353,7 @@ static void hid_prox_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
+ hid_sensor_remove_trigger(&prox_state->common_attributes);
}
static const struct platform_device_id hid_prox_ids[] = {
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index b01dd53eb100..8be3dfe4dd58 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -542,7 +542,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
+ hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
return ret;
}
@@ -555,7 +555,7 @@ static void hid_magn_3d_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
+ hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
}
static const struct platform_device_id hid_magn_3d_ids[] = {
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index 4e23a598a3fb..56fd9c53dfc2 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -378,7 +378,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
+ hid_sensor_remove_trigger(&incl_state->common_attributes);
return ret;
}
@@ -391,7 +391,7 @@ static void hid_incl_3d_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
+ hid_sensor_remove_trigger(&incl_state->common_attributes);
}
static const struct platform_device_id hid_incl_3d_ids[] = {
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index 4a11e4555099..56fdb3412fe3 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -353,7 +353,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
+ hid_sensor_remove_trigger(&rot_state->common_attributes);
return ret;
}
@@ -366,7 +366,7 @@ static void hid_dev_rot_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
+ hid_sensor_remove_trigger(&rot_state->common_attributes);
}
static const struct platform_device_id hid_dev_rot_ids[] = {
diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c
index a26d391661fd..5288b63f4e21 100644
--- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c
+++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c
@@ -337,7 +337,7 @@ static int hid_hinge_probe(struct platform_device *pdev)
error_remove_callback:
sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
+ hid_sensor_remove_trigger(&st->common_attributes);
return ret;
}
@@ -350,7 +350,7 @@ static void hid_hinge_remove(struct platform_device *pdev)
iio_device_unregister(indio_dev);
sensor_hub_remove_callback(hsdev, hsdev->usage);
- hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
+ hid_sensor_remove_trigger(&st->common_attributes);
}
static const struct platform_device_id hid_hinge_ids[] = {
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 5f1d6abda3e4..2bf5d055e175 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -319,7 +319,7 @@ static int hid_press_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
+ hid_sensor_remove_trigger(&press_state->common_attributes);
return ret;
}
@@ -332,7 +332,7 @@ static void hid_press_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
+ hid_sensor_remove_trigger(&press_state->common_attributes);
}
static const struct platform_device_id hid_press_ids[] = {
diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index 9f628a8e5cfb..60d4fcc8043b 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -253,7 +253,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
error_remove_callback:
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
error_remove_trigger:
- hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
+ hid_sensor_remove_trigger(&temp_st->common_attributes);
return ret;
}
@@ -265,7 +265,7 @@ static void hid_temperature_remove(struct platform_device *pdev)
struct temperature_state *temp_st = iio_priv(indio_dev);
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
- hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
+ hid_sensor_remove_trigger(&temp_st->common_attributes);
}
static const struct platform_device_id hid_temperature_ids[] = {
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/9] iio: introduce devm_ API for hid sensro setup and cleanup
From: Sanjay Chitroda @ 2026-05-09 10:10 UTC (permalink / raw)
To: jikos, jic23, srinivas.pandruvada
Cc: dlechner, nuno.sa, andy, sanjayembeddedse, sakari.ailus,
linux-input, linux-iio, linux-kernel
From: Sanjay Chitroda <sanjayembeddedse@gmail.com>
Key highlights:
- Prepare change as pre-requisite for devm conversion for HID IIO
drivers by removing redundant argument
- Add devm API to setup trigger and clenaup resource using
devm_add_action_or_reset()
- few cleanup and prepratory changes before updating driver for devm_
- few sample driver update using devm conversion to auto release resource
changes in v3:
- Added cleanup and prepratory changes before adding devm_ API
conversion based on self review: 0002, 0004, 0006, 0007 and 0008
- Address andy's review comment on commit message and coding style
- v2 series -> https://lore.kernel.org/all/20260429175918.2541914-1-sanjayembedded@gmail.com/
changes in v2:
- Following input from Jonathan and Andy, squash initial patch v1
series in single change as individual change should not break anything
- Add devm API support and two driver using the same
- v1 series -> https://lore.kernel.org/all/20260428071613.1134053-1-sanjayembedded@gmail.com/
Testing:
- Compiled with W=1
- Build-tested on QEMU x86_64
Based on further feedback and reviews, I would extend this series to convert all HID IIO driver to use devm_* API.
Thanks,
Sanjay Chitroda
Sanjay Chitroda (9):
iio: hid-sensors: drop redundant iio_dev argument
iio: hid-sensors: cleanup codestyle warning
iio: hid-sensors: introduce device managed API
iio: gyro: hid-sensor-gyro-3d: cleanup codestyle warning
iio: gyro: hid-sensor-gyro-3d: drop hid_sensor_remove_trigger() using
devm API
iio: humidity: hid-sensor-humidity: cleanup codestyle check
iio: humidity: hid-sensor-humidity: use common device for devres
iio: humidity: hid-sensor-humidity: use local struct device
iio: humidity: hid-sensor-humidity: drop hid_sensor_remove_trigger()
using devm API
drivers/iio/accel/hid-sensor-accel-3d.c | 4 +-
.../common/hid-sensors/hid-sensor-trigger.c | 24 +++++++-
.../common/hid-sensors/hid-sensor-trigger.h | 5 +-
drivers/iio/gyro/hid-sensor-gyro-3d.c | 16 ++---
drivers/iio/humidity/hid-sensor-humidity.c | 61 +++++++++----------
drivers/iio/light/hid-sensor-als.c | 4 +-
drivers/iio/light/hid-sensor-prox.c | 4 +-
drivers/iio/magnetometer/hid-sensor-magn-3d.c | 4 +-
drivers/iio/orientation/hid-sensor-incl-3d.c | 4 +-
drivers/iio/orientation/hid-sensor-rotation.c | 4 +-
.../position/hid-sensor-custom-intel-hinge.c | 4 +-
drivers/iio/pressure/hid-sensor-press.c | 4 +-
.../iio/temperature/hid-sensor-temperature.c | 4 +-
13 files changed, 78 insertions(+), 64 deletions(-)
base-commit: 39b80c5c9830d12d2d6531059001301c4265322a
--
2.34.1
^ permalink raw reply
* Re: [PATCH] Input: analog: replace deprecated simple_strtoul() with kstrtouint()
From: David Laight @ 2026-05-09 9:55 UTC (permalink / raw)
To: Akash Sukhavasi; +Cc: dmitry.torokhov, linux-input, linux-kernel
In-Reply-To: <20260509024048.46132-1-akash.sukhavasi@gmail.com>
On Fri, 8 May 2026 21:40:48 -0500
Akash Sukhavasi <akash.sukhavasi@gmail.com> wrote:
> The simple_strtoul() function is deprecated because it ignores
> trailing garbage characters, which can mask typos in input.
>
> Replace it with kstrtouint() in analog_parse_options() to enforce
> strict input parsing.
>
> Note that this introduces a minor, intended behavior change: while
> the old code would silently parse a malformed string like "12abc"
> as 12, the new code will reject it entirely and fall back to the
> unconfigured state (0xff). This strict parsing is the preferred
> modern behavior for kernel parameters.
However that might break existing systems.
So I'm not at all sure it should be done.
David
>
> Signed-off-by: Akash Sukhavasi <akash.sukhavasi@gmail.com>
> ---
> drivers/input/joystick/analog.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
> index b6f7bce1c..07ad360f8 100644
> --- a/drivers/input/joystick/analog.c
> +++ b/drivers/input/joystick/analog.c
> @@ -653,7 +653,7 @@ static struct analog_types analog_types[] = {
> static void analog_parse_options(void)
> {
> int i, j;
> - char *end;
> + unsigned int parsed_val;
>
> for (i = 0; i < js_nargs; i++) {
>
> @@ -664,8 +664,10 @@ static void analog_parse_options(void)
> }
> if (analog_types[j].name) continue;
>
> - analog_options[i] = simple_strtoul(js[i], &end, 0);
> - if (end != js[i]) continue;
> + if (kstrtouint(js[i], 0, &parsed_val) == 0) {
> + analog_options[i] = parsed_val;
> + continue;
> + }
>
> analog_options[i] = 0xff;
> if (!strlen(js[i])) continue;
^ permalink raw reply
* [PATCH] HID: mcp2221: fix OOB write in mcp2221_raw_event()
From: Florian Pradines @ 2026-05-09 9:45 UTC (permalink / raw)
To: gupt21, jikos, bentiss; +Cc: linux-i2c, linux-input, Florian Pradines
mcp2221_raw_event() copies device-supplied data into mcp->rxbuf at
offset rxbuf_idx without checking that the copy fits within the
destination buffer. A device responding with up to 60 bytes to a
small I2C/SMBus read can overflow the buffer.
Add a rxbuf_size field to struct mcp2221, set it alongside rxbuf in
mcp_i2c_smbus_read(), and check rxbuf_idx + data[3] <= rxbuf_size
before the memcpy.
Signed-off-by: Florian Pradines <florian.pradines@gmail.com>
---
drivers/hid/hid-mcp2221.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index be80970ab48e..e4ddd8e9293b 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -128,6 +128,7 @@ struct mcp2221 {
u8 *rxbuf;
u8 txbuf[64];
int rxbuf_idx;
+ int rxbuf_size;
int status;
u8 cur_i2c_clk_div;
struct gpio_chip *gc;
@@ -330,12 +331,14 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
mcp->txbuf[3] = (u8)(msg->addr << 1);
total_len = msg->len;
mcp->rxbuf = msg->buf;
+ mcp->rxbuf_size = msg->len;
} else {
mcp->txbuf[1] = smbus_len;
mcp->txbuf[2] = 0;
mcp->txbuf[3] = (u8)(smbus_addr << 1);
total_len = smbus_len;
mcp->rxbuf = smbus_buf;
+ mcp->rxbuf_size = smbus_len;
}
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4);
@@ -919,6 +922,10 @@ static int mcp2221_raw_event(struct hid_device *hdev,
mcp->status = -EINVAL;
break;
}
+ if (mcp->rxbuf_idx + data[3] > mcp->rxbuf_size) {
+ mcp->status = -EINVAL;
+ break;
+ }
buf = mcp->rxbuf;
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
--
2.47.3
^ permalink raw reply related
* [PATCH] HID: i2c-hid: add reset quirk for BLTP7853 touchpad
From: Xu Rao @ 2026-05-09 8:21 UTC (permalink / raw)
To: jikos, bentiss; +Cc: linux-input, linux-kernel, raoxu
The BLTP7853 I2C HID touchpad may fail to probe after reboot or
reprobe because reset completion is not signalled to the host. The
driver then waits for the reset-complete interrupt until it times out
and the device probe fails:
i2c_hid i2c-BLTP7853:00: failed to reset device.
i2c_hid i2c-BLTP7853:00: can't add hid device: -61
i2c_hid: probe of i2c-BLTP7853:00 failed with error -61
Add I2C_HID_QUIRK_NO_IRQ_AFTER_RESET for the device so i2c-hid does
not wait for a reset interrupt that may never arrive.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/hid/hid-ids.h | 3 +++
drivers/hid/i2c-hid/i2c-hid-core.c | 2 ++
2 files changed, 5 insertions(+)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 0cf63742315b..dcb0b34cd069 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -277,6 +277,9 @@
#define USB_VENDOR_ID_BIGBEN 0x146b
#define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902
+#define I2C_VENDOR_ID_BLTP 0x36b6
+#define I2C_PRODUCT_ID_BLTP7853 0xc001
+
#define USB_VENDOR_ID_BTC 0x046e
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index 5a183af3d5c6..baff2728603e 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -149,6 +149,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_BOGUS_IRQ },
{ I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_0D42,
I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME },
+ { I2C_VENDOR_ID_BLTP, I2C_PRODUCT_ID_BLTP7853,
+ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ 0, 0 }
};
--
2.50.1
^ permalink raw reply related
* [PATCH] Input: analog: replace deprecated simple_strtoul() with kstrtouint()
From: Akash Sukhavasi @ 2026-05-09 2:40 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Akash Sukhavasi
The simple_strtoul() function is deprecated because it ignores
trailing garbage characters, which can mask typos in input.
Replace it with kstrtouint() in analog_parse_options() to enforce
strict input parsing.
Note that this introduces a minor, intended behavior change: while
the old code would silently parse a malformed string like "12abc"
as 12, the new code will reject it entirely and fall back to the
unconfigured state (0xff). This strict parsing is the preferred
modern behavior for kernel parameters.
Signed-off-by: Akash Sukhavasi <akash.sukhavasi@gmail.com>
---
drivers/input/joystick/analog.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index b6f7bce1c..07ad360f8 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -653,7 +653,7 @@ static struct analog_types analog_types[] = {
static void analog_parse_options(void)
{
int i, j;
- char *end;
+ unsigned int parsed_val;
for (i = 0; i < js_nargs; i++) {
@@ -664,8 +664,10 @@ static void analog_parse_options(void)
}
if (analog_types[j].name) continue;
- analog_options[i] = simple_strtoul(js[i], &end, 0);
- if (end != js[i]) continue;
+ if (kstrtouint(js[i], 0, &parsed_val) == 0) {
+ analog_options[i] = parsed_val;
+ continue;
+ }
analog_options[i] = 0xff;
if (!strlen(js[i])) continue;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH RESEND 2] HID: Add force feedback support for Speedlink Cougar Vibration Flightstick
From: Dmitry Torokhov @ 2026-05-08 23:14 UTC (permalink / raw)
To: sashiko; +Cc: Harald Judt, linux-input
In-Reply-To: <20260508225130.C6D33C2BCB0@smtp.kernel.org>
On Fri, May 08, 2026 at 10:51:30PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Race condition in force feedback initialization leading to a NULL pointer dereference.
> - [High] Ignoring the return value of `gembird_joy_init()` causes a NULL pointer dereference when user-space interacts with the event device after an initialization failure.
> - [High] Non-monotonic magnitude scaling logic severely breaks force feedback functionality.
> - [Medium] Memory leak of the `gembird_joy_device` state structure upon device disconnect.
> --
>
> commit f9141496ab06cd22000d2487bbb234d7e91931b5
> Author: Harald Judt <h.judt@gmx.at>
>
> HID: Add force feedback support for Gembird based joystick
>
> This commit adds force feedback (rumble) support for the SpeedLink Cougar
> Vibration Flightstick (SL-6630) joystick, which uses a Gembird controller.
>
> > diff --git a/drivers/hid/hid-gembird-joy.c b/drivers/hid/hid-gembird-joy.c
> > new file mode 100644
> > index 0000000000000..5a5afa02f8407
> > --- /dev/null
> > +++ b/drivers/hid/hid-gembird-joy.c
>
> [ ... ]
>
> > +static int hid_gembird_joy_play(struct input_dev *dev, void *data,
> > + struct ff_effect *effect)
> > +{
> > + struct hid_device *hid = input_get_drvdata(dev);
> > + struct gembird_joy_device *joy = data;
> > + int strong, weak;
> > +
> > + strong = effect->u.rumble.strong_magnitude;
> > + weak = effect->u.rumble.weak_magnitude;
>
> [ ... ]
>
> > + strong = (strong / 0xff) * 0x1f / 0xff;
> > + weak = (weak / 0xff) * 0x1f / 0xff;
> > +
> > + /* ... and to support the notions of strong vs weak rumble effects,
> > + * increase the magnitude for the strong rumble effect if it is below the
> > + * half of the maximum value, as the strong motor has the same strength as
> > + * the weak one. Likewise, decrease the magnitude for the weak effect.
> > + */
> > + if (strong < 0x10 && !weak) /* fftest effect 4 strong rumble */
> > + strong *= 2;
> > + else if (!strong && weak >= 0x10) /* fftest effect 5 weak rumble */
> > + weak /= 2;
>
> Will this cause issues with non-monotonic force feedback behavior and
> unintended truncation?
>
> Since the math uses integer division, any input value below 255 for strong
> or weak will evaluate to zero during the first division by 0xff.
>
> Additionally, could the conditional scaling cause abrupt changes in rumble
> strength? For instance, if a game requests a strong magnitude that scales
> down to 15, it gets doubled to 30 because of the strong < 0x10 check.
> However, a slightly stronger requested magnitude that scales to 16 will
> remain 16, meaning an increase in requested strength results in a drop in
> physical rumble.
>
> [ ... ]
>
> > +static int gembird_joy_init(struct hid_device *hid)
> > +{
>
> [ ... ]
>
> > + joy = kzalloc(sizeof(struct gembird_joy_device), GFP_KERNEL);
> > + if (!joy)
> > + return -ENOMEM;
>
> Does this memory leak when the device is disconnected or the driver is
> unbound? The allocation doesn't use devm_kzalloc, and there isn't a
> remove callback to free it. The input_ff_create_memless teardown
> handles its own structures but not the user-provided private data.
Sashiko is wrong here.
>
> > +
> > + dev = hidinput->input;
> > +
> > + set_bit(FF_RUMBLE, dev->ffbit);
> > +
> > + error = input_ff_create_memless(dev, joy, hid_gembird_joy_play);
> > + if (error) {
> > + kfree(joy);
> > + return error;
> > + }
> > +
> > + joy->report = report;
>
> Is there a window here where an ioctl could trigger a NULL pointer
> dereference?
>
> The device was already started earlier in gembird_joy_probe via
> hid_hw_start, so userspace can issue an EVIOCSFF ioctl as soon as
> input_ff_create_memless succeeds. If the ioctl triggers
> hid_gembird_joy_play before joy->report is assigned on the next line,
> the callback will try to access joy->report->field and dereference NULL.
Sashiko is wrong here as well. The device is not accessible from
userspace until it is registered.
>
> [ ... ]
>
> > +static int gembird_joy_probe(struct hid_device *hdev, const struct hid_device_id *id)
> > +{
> > + int ret;
> > +
> > + ret = hid_parse(hdev);
> > + if (ret) {
> > + hid_err(hdev, "parse failed\n");
> > + return ret;
> > + }
> > +
> > + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
> > + if (ret) {
> > + hid_err(hdev, "hw start failed\n");
> > + return ret;
> > + }
> > +
> > + gembird_joy_init(hdev);
> > +
> > + return 0;
> > +}
>
> What happens if gembird_joy_init fails?
>
> If input_ff_create_memless returns an error, gembird_joy_init
> propagates it, but the return value is ignored here and probe succeeds.
>
> Because set_bit(FF_RUMBLE, dev->ffbit) was already called, the device
> remains registered and advertises rumble support. If userspace then issues
> an EVIOCSFF ioctl, the kernel will allow it, which could lead to a panic
> when it tries to dereference the missing force feedback structures.
>
Need to have error handling.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH RESEND 2] HID: Add force feedback support for Speedlink Cougar Vibration Flightstick
From: sashiko-bot @ 2026-05-08 22:51 UTC (permalink / raw)
To: Harald Judt; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <b3bfa73b-0547-45f9-b6ae-f28815e6c632@gmx.at>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Race condition in force feedback initialization leading to a NULL pointer dereference.
- [High] Ignoring the return value of `gembird_joy_init()` causes a NULL pointer dereference when user-space interacts with the event device after an initialization failure.
- [High] Non-monotonic magnitude scaling logic severely breaks force feedback functionality.
- [Medium] Memory leak of the `gembird_joy_device` state structure upon device disconnect.
--
commit f9141496ab06cd22000d2487bbb234d7e91931b5
Author: Harald Judt <h.judt@gmx.at>
HID: Add force feedback support for Gembird based joystick
This commit adds force feedback (rumble) support for the SpeedLink Cougar
Vibration Flightstick (SL-6630) joystick, which uses a Gembird controller.
> diff --git a/drivers/hid/hid-gembird-joy.c b/drivers/hid/hid-gembird-joy.c
> new file mode 100644
> index 0000000000000..5a5afa02f8407
> --- /dev/null
> +++ b/drivers/hid/hid-gembird-joy.c
[ ... ]
> +static int hid_gembird_joy_play(struct input_dev *dev, void *data,
> + struct ff_effect *effect)
> +{
> + struct hid_device *hid = input_get_drvdata(dev);
> + struct gembird_joy_device *joy = data;
> + int strong, weak;
> +
> + strong = effect->u.rumble.strong_magnitude;
> + weak = effect->u.rumble.weak_magnitude;
[ ... ]
> + strong = (strong / 0xff) * 0x1f / 0xff;
> + weak = (weak / 0xff) * 0x1f / 0xff;
> +
> + /* ... and to support the notions of strong vs weak rumble effects,
> + * increase the magnitude for the strong rumble effect if it is below the
> + * half of the maximum value, as the strong motor has the same strength as
> + * the weak one. Likewise, decrease the magnitude for the weak effect.
> + */
> + if (strong < 0x10 && !weak) /* fftest effect 4 strong rumble */
> + strong *= 2;
> + else if (!strong && weak >= 0x10) /* fftest effect 5 weak rumble */
> + weak /= 2;
Will this cause issues with non-monotonic force feedback behavior and
unintended truncation?
Since the math uses integer division, any input value below 255 for strong
or weak will evaluate to zero during the first division by 0xff.
Additionally, could the conditional scaling cause abrupt changes in rumble
strength? For instance, if a game requests a strong magnitude that scales
down to 15, it gets doubled to 30 because of the strong < 0x10 check.
However, a slightly stronger requested magnitude that scales to 16 will
remain 16, meaning an increase in requested strength results in a drop in
physical rumble.
[ ... ]
> +static int gembird_joy_init(struct hid_device *hid)
> +{
[ ... ]
> + joy = kzalloc(sizeof(struct gembird_joy_device), GFP_KERNEL);
> + if (!joy)
> + return -ENOMEM;
Does this memory leak when the device is disconnected or the driver is
unbound? The allocation doesn't use devm_kzalloc, and there isn't a
remove callback to free it. The input_ff_create_memless teardown
handles its own structures but not the user-provided private data.
> +
> + dev = hidinput->input;
> +
> + set_bit(FF_RUMBLE, dev->ffbit);
> +
> + error = input_ff_create_memless(dev, joy, hid_gembird_joy_play);
> + if (error) {
> + kfree(joy);
> + return error;
> + }
> +
> + joy->report = report;
Is there a window here where an ioctl could trigger a NULL pointer
dereference?
The device was already started earlier in gembird_joy_probe via
hid_hw_start, so userspace can issue an EVIOCSFF ioctl as soon as
input_ff_create_memless succeeds. If the ioctl triggers
hid_gembird_joy_play before joy->report is assigned on the next line,
the callback will try to access joy->report->field and dereference NULL.
[ ... ]
> +static int gembird_joy_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> + int ret;
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "parse failed\n");
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + return ret;
> + }
> +
> + gembird_joy_init(hdev);
> +
> + return 0;
> +}
What happens if gembird_joy_init fails?
If input_ff_create_memless returns an error, gembird_joy_init
propagates it, but the return value is ignored here and probe succeeds.
Because set_bit(FF_RUMBLE, dev->ffbit) was already called, the device
remains registered and advertises rumble support. If userspace then issues
an EVIOCSFF ioctl, the kernel will allow it, which could lead to a panic
when it tries to dereference the missing force feedback structures.
--
Sashiko AI review · https://sashiko.dev/#/patchset/b3bfa73b-0547-45f9-b6ae-f28815e6c632@gmx.at?part=1
^ permalink raw reply
* Re: [PATCH RFC 4/4] arm64: dts: qcom: milos-fairphone-fp6: add supply for Hall Effect sensor
From: sashiko-bot @ 2026-05-08 21:56 UTC (permalink / raw)
To: Griffin Kroah-Hartman
Cc: robh, conor+dt, linux-input, krzk+dt, devicetree, dmitry.torokhov
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-4-0bb32e8e6428@fairphone.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Removing `regulator-always-on` from the Hall sensor's supply causes interrupt storms, spurious events, and broken system wakeup when the input device is closed.
--
commit 35bf0d08eef4da2139153f5425e351a93d9d591d
Author: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
arm64: dts: qcom: milos-fairphone-fp6: add supply for Hall Effect sensor
This commit adds a vdd-supply property to the Hall Effect sensor and removes
the regulator-always-on property from vreg_l10b, relying on the gpio-keys
driver to manage power.
> diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
> index c1899db46e714..ae6900c3f75c6 100644
> --- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
> +++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
> @@ -32,13 +32,13 @@ gpio-keys {
> pinctrl-0 = <&volume_up_default>, <&hall_sensor_default>;
> pinctrl-names = "default";
>
> - /* Powered by the always-on vreg_l10b */
> event-hall-sensor {
> label = "Hall Effect Sensor";
> gpios = <&tlmm 70 GPIO_ACTIVE_LOW>;
> linux,input-type = <EV_SW>;
> linux,code = <SW_LID>;
> linux,can-disable;
> + vdd-supply = <&vreg_l10b>;
When the device is opened, the driver reads the GPIO state immediately after
enabling the regulator. Without a startup-delay-us property on the regulator
to allow the voltage to stabilize, could this read an uninitialized state and
generate spurious lid events?
> wakeup-source;
> };
>
[ ... ]
> @@ -326,8 +326,6 @@ vreg_l10b: ldo10 {
> regulator-min-microvolt = <1800000>;
> regulator-max-microvolt = <1800000>;
> regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
> - /* Hall sensor VDD */
> - regulator-always-on;
> };
Could removing this property cause an interrupt storm?
The gpio-keys driver requests and unmasks the GPIO interrupt during probe, but
only enables the vdd-supply regulator when the input device is explicitly
opened by userspace.
If the device is closed, the regulator is disabled. Cutting power to the Hall
sensor while its edge-triggered interrupt remains active might cause the GPIO
line to float. A floating line can pick up electrical noise and trigger a
continuous interrupt storm.
Additionally, does this break the system's ability to wake up?
If the system suspends while the device is closed, the sensor remains
unpowered. Even though the wake IRQ is armed due to the wakeup-source
property, an unpowered sensor wouldn't be able to detect lid events to wake
the system.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com?part=4
^ permalink raw reply
* Re: [PATCH RFC 3/4] Input: gpio-keys - add regulator to gpio_keys
From: sashiko-bot @ 2026-05-08 21:36 UTC (permalink / raw)
To: Griffin Kroah-Hartman
Cc: linux-input, robh, krzk+dt, dmitry.torokhov, conor+dt, devicetree
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-3-0bb32e8e6428@fairphone.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Tying the regulator's power state to the input device's open()/close() lifecycle leaves interrupts active while the hardware is unpowered, causing an interrupt storm.
- [Medium] System wakeup functionality is broken because regulators are disabled if the input device is closed.
- [Medium] Unsafe hardware access during system resume causes spurious key events.
- [Medium] Missing pdata->disable() in the gpio_keys_open() error path leads to a resource leak.
- [Low] Incorrect teardown order in gpio_keys_close() violates LIFO symmetry.
--
commit d47e6012a488ac21d0005ba6dd4aa95a285831d3
Author: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
Input: gpio-keys - add regulator to gpio_keys
This commit allows gpio-keys to have a vdd power supply managed through
regulators.
> diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
> index e196174856796..2199ce7455cfc 100644
> --- a/drivers/input/keyboard/gpio_keys.c
> +++ b/drivers/input/keyboard/gpio_keys.c
[ ... ]
> @@ -729,6 +730,7 @@ static int gpio_keys_open(struct input_dev *input)
> struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
> const struct gpio_keys_platform_data *pdata = ddata->pdata;
> int error;
> + int i;
>
> if (pdata->enable) {
> error = pdata->enable(input->dev.parent);
> @@ -736,19 +738,48 @@ static int gpio_keys_open(struct input_dev *input)
> return error;
> }
>
> + for (i = 0; i < pdata->nbuttons; i++) {
> + const struct gpio_keys_button *button = &pdata->buttons[i];
> +
> + if (!button->regulator)
> + continue;
> + error = regulator_enable(button->regulator);
> + if (error)
> + goto reg_err;
> + }
Because interrupts are requested unconditionally in gpio_keys_probe() and
remain unmasked, tying the regulator power state to the input device's
open()/close() lifecycle leaves the physical button unpowered when the device
is closed. Could this allow the GPIO pin to float and trigger an interrupt
storm?
Also, if the input device is closed before suspend, the regulator remains
disabled. Does this prevent an unpowered physical button from generating the
electrical signal required to trigger a wakeup interrupt?
Additionally, gpio_keys_resume() unconditionally calls
gpio_keys_report_state(), which reads the current state of all GPIOs. If the
device is closed, the regulators are powered off. Can reading unpowered GPIOs
return floating states that inject spurious phantom key press events on every
resume?
> +
> /* Report current state of buttons that are connected to GPIOs */
> gpio_keys_report_state(ddata);
>
> return 0;
> +
> +reg_err:
> + for (--i; i >= 0; i--) {
> + const struct gpio_keys_button *button = &pdata->buttons[i];
> +
> + if (!button->regulator)
> + continue;
> + regulator_disable(button->regulator);
> + }
> + return error;
If regulator_enable() fails and we jump to reg_err, we return the error but
fail to call pdata->disable(). Does this leave the platform state permanently
active and leak resources since pdata->enable() was called earlier?
> }
>
> static void gpio_keys_close(struct input_dev *input)
> {
> struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
> const struct gpio_keys_platform_data *pdata = ddata->pdata;
> + int i;
>
> if (pdata->disable)
> pdata->disable(input->dev.parent);
> +
> + for (i = 0; i < pdata->nbuttons; i++) {
> + const struct gpio_keys_button *button = &pdata->buttons[i];
> +
> + if (!button->regulator)
> + continue;
> + regulator_disable(button->regulator);
> + }
> }
Is the teardown order correct here? In gpio_keys_open(), pdata->enable() is
called before the regulators are enabled. Should gpio_keys_close() reverse
this sequence by disabling the regulators before calling pdata->disable() to
maintain LIFO symmetry?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com?part=3
^ permalink raw reply
* [dtor-input:for-linus] BUILD SUCCESS 74d695fd6f9d70df849c555f358ddfd26e2d85bf
From: kernel test robot @ 2026-05-08 21:22 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
branch HEAD: 74d695fd6f9d70df849c555f358ddfd26e2d85bf Input: fm801-gp - simplify initialisation of pci_device_id array
elapsed time: 1197m
configs tested: 334
configs skipped: 9
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-19
arc allyesconfig clang-23
arc allyesconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc haps_hs_smp_defconfig gcc-15.2.0
arc nsimosci_hs_smp_defconfig gcc-15.2.0
arc randconfig-001 gcc-8.5.0
arc randconfig-001-20260508 gcc-12.5.0
arc randconfig-001-20260508 gcc-8.5.0
arc randconfig-001-20260509 gcc-9.5.0
arc randconfig-002 gcc-8.5.0
arc randconfig-002-20260508 gcc-12.5.0
arc randconfig-002-20260508 gcc-8.5.0
arc randconfig-002-20260509 gcc-9.5.0
arm allnoconfig clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm h3600_defconfig gcc-15.2.0
arm neponset_defconfig gcc-15.2.0
arm randconfig-001 gcc-8.5.0
arm randconfig-001-20260508 gcc-12.5.0
arm randconfig-001-20260508 gcc-8.5.0
arm randconfig-001-20260509 gcc-9.5.0
arm randconfig-002 gcc-8.5.0
arm randconfig-002-20260508 gcc-12.5.0
arm randconfig-002-20260508 gcc-8.5.0
arm randconfig-002-20260509 gcc-9.5.0
arm randconfig-003 gcc-8.5.0
arm randconfig-003-20260508 gcc-12.5.0
arm randconfig-003-20260508 gcc-8.5.0
arm randconfig-003-20260509 gcc-9.5.0
arm randconfig-004 gcc-8.5.0
arm randconfig-004-20260508 gcc-12.5.0
arm randconfig-004-20260508 gcc-8.5.0
arm randconfig-004-20260509 gcc-9.5.0
arm64 allmodconfig clang-19
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260508 gcc-14.3.0
arm64 randconfig-001-20260509 gcc-10.5.0
arm64 randconfig-002-20260508 gcc-14.3.0
arm64 randconfig-002-20260509 gcc-10.5.0
arm64 randconfig-003-20260508 gcc-14.3.0
arm64 randconfig-003-20260509 gcc-10.5.0
arm64 randconfig-004-20260508 gcc-14.3.0
arm64 randconfig-004-20260509 gcc-10.5.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260508 gcc-14.3.0
csky randconfig-001-20260509 gcc-10.5.0
csky randconfig-002-20260508 gcc-14.3.0
csky randconfig-002-20260509 gcc-10.5.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260508 clang-23
hexagon randconfig-001-20260508 gcc-11.5.0
hexagon randconfig-001-20260509 clang-17
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260508 clang-23
hexagon randconfig-002-20260508 gcc-11.5.0
hexagon randconfig-002-20260509 clang-17
i386 allmodconfig clang-20
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 buildonly-randconfig-001 gcc-14
i386 buildonly-randconfig-001-20260508 gcc-14
i386 buildonly-randconfig-001-20260509 gcc-14
i386 buildonly-randconfig-002 gcc-14
i386 buildonly-randconfig-002-20260508 gcc-14
i386 buildonly-randconfig-002-20260509 gcc-14
i386 buildonly-randconfig-003 gcc-14
i386 buildonly-randconfig-003-20260508 gcc-14
i386 buildonly-randconfig-003-20260509 gcc-14
i386 buildonly-randconfig-004 gcc-14
i386 buildonly-randconfig-004-20260508 gcc-14
i386 buildonly-randconfig-004-20260509 gcc-14
i386 buildonly-randconfig-005 gcc-14
i386 buildonly-randconfig-005-20260508 gcc-14
i386 buildonly-randconfig-005-20260509 gcc-14
i386 buildonly-randconfig-006 gcc-14
i386 buildonly-randconfig-006-20260508 gcc-14
i386 buildonly-randconfig-006-20260509 gcc-14
i386 defconfig gcc-15.2.0
i386 randconfig-001 gcc-14
i386 randconfig-001-20260508 gcc-14
i386 randconfig-001-20260509 clang-20
i386 randconfig-002 gcc-14
i386 randconfig-002-20260508 gcc-14
i386 randconfig-002-20260509 clang-20
i386 randconfig-003 gcc-14
i386 randconfig-003-20260508 gcc-14
i386 randconfig-003-20260509 clang-20
i386 randconfig-004 gcc-14
i386 randconfig-004-20260508 gcc-14
i386 randconfig-004-20260509 clang-20
i386 randconfig-005 gcc-14
i386 randconfig-005-20260508 gcc-14
i386 randconfig-005-20260509 clang-20
i386 randconfig-006 gcc-14
i386 randconfig-006-20260508 gcc-14
i386 randconfig-006-20260509 clang-20
i386 randconfig-007 gcc-14
i386 randconfig-007-20260508 gcc-14
i386 randconfig-007-20260509 clang-20
i386 randconfig-011-20260508 gcc-13
i386 randconfig-012-20260508 gcc-13
i386 randconfig-013-20260508 gcc-13
i386 randconfig-014-20260508 gcc-13
i386 randconfig-015-20260508 gcc-13
i386 randconfig-016-20260508 gcc-13
i386 randconfig-017-20260508 gcc-13
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260508 clang-23
loongarch randconfig-001-20260508 gcc-11.5.0
loongarch randconfig-001-20260509 clang-17
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260508 clang-23
loongarch randconfig-002-20260508 gcc-11.5.0
loongarch randconfig-002-20260509 clang-17
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips loongson3_defconfig gcc-15.2.0
mips omega2p_defconfig clang-23
mips rt305x_defconfig clang-23
nios2 allmodconfig clang-23
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-19
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260508 clang-23
nios2 randconfig-001-20260508 gcc-11.5.0
nios2 randconfig-001-20260509 clang-17
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260508 clang-23
nios2 randconfig-002-20260508 gcc-11.5.0
nios2 randconfig-002-20260509 clang-17
openrisc allmodconfig clang-23
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
openrisc simple_smp_defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-15.2.0
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001 gcc-9.5.0
parisc randconfig-001-20260508 gcc-9.5.0
parisc randconfig-001-20260509 gcc-11.5.0
parisc randconfig-002 gcc-9.5.0
parisc randconfig-002-20260508 gcc-9.5.0
parisc randconfig-002-20260509 gcc-11.5.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-15.2.0
powerpc holly_defconfig clang-23
powerpc randconfig-001 gcc-9.5.0
powerpc randconfig-001-20260508 gcc-9.5.0
powerpc randconfig-001-20260509 gcc-11.5.0
powerpc randconfig-002 gcc-9.5.0
powerpc randconfig-002-20260508 gcc-9.5.0
powerpc randconfig-002-20260509 gcc-11.5.0
powerpc tqm8555_defconfig gcc-15.2.0
powerpc64 randconfig-001 gcc-9.5.0
powerpc64 randconfig-001-20260508 gcc-9.5.0
powerpc64 randconfig-001-20260509 gcc-11.5.0
powerpc64 randconfig-002 gcc-9.5.0
powerpc64 randconfig-002-20260508 gcc-9.5.0
powerpc64 randconfig-002-20260509 gcc-11.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260508 clang-23
riscv randconfig-001-20260509 clang-23
riscv randconfig-002-20260508 clang-23
riscv randconfig-002-20260509 clang-23
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260508 clang-23
s390 randconfig-001-20260509 clang-23
s390 randconfig-002-20260508 clang-23
s390 randconfig-002-20260509 clang-23
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allnoconfig gcc-15.2.0
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh apsh4ad0a_defconfig gcc-15.2.0
sh defconfig gcc-14
sh magicpanelr2_defconfig gcc-15.2.0
sh polaris_defconfig gcc-15.2.0
sh randconfig-001-20260508 clang-23
sh randconfig-001-20260509 clang-23
sh randconfig-002-20260508 clang-23
sh randconfig-002-20260509 clang-23
sparc allnoconfig clang-23
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-15.2.0
sparc randconfig-001 gcc-12
sparc randconfig-001-20260508 gcc-12
sparc randconfig-001-20260509 clang-23
sparc randconfig-002 gcc-12
sparc randconfig-002-20260508 gcc-12
sparc randconfig-002-20260509 clang-23
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001 gcc-12
sparc64 randconfig-001-20260508 gcc-12
sparc64 randconfig-001-20260509 clang-23
sparc64 randconfig-002 gcc-12
sparc64 randconfig-002-20260508 gcc-12
sparc64 randconfig-002-20260509 clang-23
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001 gcc-12
um randconfig-001-20260508 gcc-12
um randconfig-001-20260509 clang-23
um randconfig-002 gcc-12
um randconfig-002-20260508 gcc-12
um randconfig-002-20260509 clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001 gcc-14
x86_64 buildonly-randconfig-001-20260508 gcc-14
x86_64 buildonly-randconfig-002 gcc-14
x86_64 buildonly-randconfig-002-20260508 gcc-14
x86_64 buildonly-randconfig-003 gcc-14
x86_64 buildonly-randconfig-003-20260508 gcc-14
x86_64 buildonly-randconfig-004 gcc-14
x86_64 buildonly-randconfig-004-20260508 gcc-14
x86_64 buildonly-randconfig-005 gcc-14
x86_64 buildonly-randconfig-005-20260508 gcc-14
x86_64 buildonly-randconfig-006 gcc-14
x86_64 buildonly-randconfig-006-20260508 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001 clang-20
x86_64 randconfig-001-20260508 clang-20
x86_64 randconfig-002 clang-20
x86_64 randconfig-002-20260508 clang-20
x86_64 randconfig-003 clang-20
x86_64 randconfig-003-20260508 clang-20
x86_64 randconfig-004 clang-20
x86_64 randconfig-004-20260508 clang-20
x86_64 randconfig-005 clang-20
x86_64 randconfig-005-20260508 clang-20
x86_64 randconfig-006 clang-20
x86_64 randconfig-006-20260508 clang-20
x86_64 randconfig-011 gcc-14
x86_64 randconfig-011-20260508 gcc-14
x86_64 randconfig-012 gcc-14
x86_64 randconfig-012-20260508 gcc-14
x86_64 randconfig-013 gcc-14
x86_64 randconfig-013-20260508 gcc-14
x86_64 randconfig-014 gcc-14
x86_64 randconfig-014-20260508 gcc-14
x86_64 randconfig-015 gcc-14
x86_64 randconfig-015-20260508 gcc-14
x86_64 randconfig-016 gcc-14
x86_64 randconfig-016-20260508 gcc-14
x86_64 randconfig-071 gcc-14
x86_64 randconfig-071-20260508 gcc-14
x86_64 randconfig-071-20260509 clang-20
x86_64 randconfig-072 gcc-14
x86_64 randconfig-072-20260508 gcc-14
x86_64 randconfig-072-20260509 clang-20
x86_64 randconfig-073 gcc-14
x86_64 randconfig-073-20260508 gcc-14
x86_64 randconfig-073-20260509 clang-20
x86_64 randconfig-074 gcc-14
x86_64 randconfig-074-20260508 gcc-14
x86_64 randconfig-074-20260509 clang-20
x86_64 randconfig-075 gcc-14
x86_64 randconfig-075-20260508 gcc-14
x86_64 randconfig-075-20260509 clang-20
x86_64 randconfig-076 gcc-14
x86_64 randconfig-076-20260508 gcc-14
x86_64 randconfig-076-20260509 clang-20
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-15.2.0
xtensa allyesconfig clang-23
xtensa randconfig-001 gcc-12
xtensa randconfig-001-20260508 gcc-12
xtensa randconfig-001-20260509 clang-23
xtensa randconfig-002 gcc-12
xtensa randconfig-002-20260508 gcc-12
xtensa randconfig-002-20260509 clang-23
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH RFC 1/4] regulator: add devm_fwnode family of functions
From: sashiko-bot @ 2026-05-08 21:08 UTC (permalink / raw)
To: Griffin Kroah-Hartman
Cc: robh, devicetree, dmitry.torokhov, krzk+dt, conor+dt, linux-input
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-1-0bb32e8e6428@fairphone.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The fallback stubs for `devm_fwnode_regulator_get` and its variants incorrectly return `NULL` instead of `ERR_PTR(-ENODEV)` when `CONFIG_OF` is disabled but `CONFIG_REGULATOR` is enabled, leading to a guaranteed kernel panic on non-OF systems.
--
commit f8899ceec7e07e935c3ae44032450cd2b8f68352
Author: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
regulator: add devm_fwnode family of functions
Add devm_fwnode_regulator_get and variants.
These function wrappers allow regulators to be accessed from the fwnode
struct without any casts.
> diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
> --- a/include/linux/regulator/consumer.h
> +++ b/include/linux/regulator/consumer.h
[ ... ]
> +static inline struct regulator *__must_check
> +devm_fwnode_regulator_get(struct device *dev, struct fwnode_handle *fwnode,
> + const char *id)
> +{
> + return NULL;
> +}
Will returning NULL here instead of ERR_PTR(-ENODEV) cause a panic on non-OF
systems?
If CONFIG_REGULATOR is enabled but CONFIG_OF is disabled, a driver calling
this will get NULL. Since drivers typically check the return value with
IS_ERR(), IS_ERR(NULL) evaluates to false. The driver would assume it
successfully acquired a valid regulator.
Later, when the driver calls regulator_enable(), it will pass this NULL
pointer. Since CONFIG_REGULATOR is enabled, regulator_enable() is not stubbed
out and will unconditionally dereference the pointer, leading to a NULL
pointer dereference.
Should these stubs return ERR_PTR(-ENODEV) instead to safely signal the
absence of a regulator and match the expected regulator API semantics?
> +
> +static inline struct regulator *__must_check
> +devm_fwnode_regulator_get_exclusive(struct device *dev, struct fwnode_handle *fwnode,
> + const char *id)
> +{
> + return NULL;
> +}
> +
> +static inline struct regulator *__must_check
> +devm_fwnode_regulator_get_optional(struct device *dev, struct fwnode_handle *fwnode,
> + const char *id)
> +{
> + return NULL;
> +}
Does returning NULL for the optional and exclusive variants violate the
regulator API semantics which require returning ERR_PTR(-ENODEV) to safely
signal the absence of a regulator?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com?part=1
^ permalink raw reply
* [PATCH RESEND 2] HID: Add force feedback support for Speedlink Cougar Vibration Flightstick
From: Harald Judt @ 2026-05-08 17:54 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires; +Cc: linux-input
Hi,
I have implemented force feedback/rumble support for the Speedlink
Cougar Vibration Flightstick joystick. While I am not quite sure about
the correctness of my approach regarding the strong and weak motors, it
seems to work as expected; I ran the fftest samples and also tested it
successfully with SuperTuxKart.
Here is the code, hopefully I have set up thunderbird mail correctly to
not mangle the patch...
From 48d8512fbe49ae7b940dc5869fe50aa905d3d5fb Mon Sep 17 00:00:00 2001
From: Harald Judt <h.judt@gmx.at>
Date: Sun, 18 Jan 2026 02:47:20 +0100
Subject: [PATCH] HID: Add force feedback support for Gembird based joystick
This commit adds force feedback support for a Gembird based joystick, namely
the SpeedLink Cougar Vibration Flightstick (SL-6630), which sports vibration
motors for rumble effects. Though it is not easy to determine, it seems to have
one motor in the base and the other in the stick, both are of equal
strength. The implementation tries to take this into account for realising weak
and strong rumble effects and has been tested using fftest and SuperTuxKart.
Signed-off-by: Harald Judt <h.judt@gmx.at>
---
drivers/hid/Kconfig | 8 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-gembird-joy.c | 178 ++++++++++++++++++++++++++++++++++
drivers/hid/hid-ids.h | 3 +
drivers/hid/hid-quirks.c | 3 +
5 files changed, 193 insertions(+)
create mode 100644 drivers/hid/hid-gembird-joy.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 04420a713be0..b4e2c8f67728 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -406,6 +406,14 @@ config HID_GEMBIRD
help
Support for Gembird JPD-DualForce 2.
+config HID_GEMBIRD_JOY_FF
+ tristate "Gembird Joysticks force feedback support"
+ depends on USB_HID
+ select INPUT_FF_MEMLESS
+ help
+ Force feedback support for Gembird (Vendor ID 0x12bd) based devices:
+ - Speed Link Cougar Vibration Flightstick (SL-6630)
+
config HID_GFRM
tristate "Google Fiber TV Box remote control support"
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 361a7daedeb8..593a429661ed 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_HID_EVISION) += hid-evision.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
+obj-$(CONFIG_HID_GEMBIRD_JOY_FF) += hid-gembird-joy.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
diff --git a/drivers/hid/hid-gembird-joy.c b/drivers/hid/hid-gembird-joy.c
new file mode 100644
index 000000000000..5a5afa02f840
--- /dev/null
+++ b/drivers/hid/hid-gembird-joy.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Gembird based Joysticks
+ *
+ * Currently supported devices:
+ * - 12bd:a02f Speed Link Cougar Vibration Flightstick (SL-6630)
+ *
+ * Copyright (c) 2026 Harald Judt <h.judt@gmx.at>
+ */
+
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+
+struct gembird_joy_device {
+ struct hid_report *report;
+};
+
+static int hid_gembird_joy_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct gembird_joy_device *joy = data;
+ int strong, weak;
+
+ strong = effect->u.rumble.strong_magnitude;
+ weak = effect->u.rumble.weak_magnitude;
+
+ hid_dbg(hid, "called with 0x%04x 0x%04x\n", strong, weak);
+
+ /* Likely there are two motors, one in the base and the other in an upper
+ * part of the stick, yet they are not different in strength, and honestly
+ * it is hard to determine which motor is active or correlates to value 0
+ * or 1 of the report field. However, the third value does not have any
+ * function, thus it will always be set to 0.
+ *
+ * The windows drivers want to make believe the first value in the report
+ * field should be influenced by horizontal axis movement, and the second
+ * by vertical axis movement, but this might just be some arbitrary gimmick
+ * of the configuration dialog, which describes the motors as left and
+ * right.
+ *
+ * Ranges are the same for both motors, weak and strong (0 to 31), so scale
+ * down the magnitudes accordingly...
+ */
+ strong = (strong / 0xff) * 0x1f / 0xff;
+ weak = (weak / 0xff) * 0x1f / 0xff;
+
+ /* ... and to support the notions of strong vs weak rumble effects,
+ * increase the magnitude for the strong rumble effect if it is below the
+ * half of the maximum value, as the strong motor has the same strength as
+ * the weak one. Likewise, decrease the magnitude for the weak effect.
+ */
+ if (strong < 0x10 && !weak) /* fftest effect 4 strong rumble */
+ strong *= 2;
+ else if (!strong && weak >= 0x10) /* fftest effect 5 weak rumble */
+ weak /= 2;
+
+ /* Use unmodified values if both magnitudes have been set. */
+ joy->report->field[0]->value[0] = strong;
+ joy->report->field[0]->value[1] = weak;
+ joy->report->field[0]->value[2] = 0;
+
+ hid_dbg(hid, "running with 0x%02x, 0x%02x\n", strong, weak);
+ hid_hw_request(hid, joy->report, HID_REQ_SET_REPORT);
+
+ return 0;
+}
+
+static int gembird_joy_init(struct hid_device *hid)
+{
+ struct gembird_joy_device *joy;
+ struct hid_report *report;
+ struct hid_input *hidinput;
+ struct list_head *report_list =
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct list_head *report_ptr = report_list;
+ struct input_dev *dev;
+ int error;
+
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+ dev = hidinput->input;
+
+ if (list_empty(report_list)) {
+ hid_err(hid, "no output reports found\n");
+ return -ENODEV;
+ }
+
+ list_for_each_entry(hidinput, &hid->inputs, list) {
+ report_ptr = report_ptr->next;
+
+ if (report_ptr == report_list) {
+ hid_err(hid, "required output report is missing\n");
+ return -ENODEV;
+ }
+
+ report = list_entry(report_ptr, struct hid_report, list);
+ if (report->maxfield < 1) {
+ hid_err(hid, "no fields in the report\n");
+ return -ENODEV;
+ }
+
+ if (report->field[0]->report_count < 3) {
+ hid_err(hid, "not enough values in the field\n");
+ return -ENODEV;
+ }
+
+ joy = kzalloc(sizeof(struct gembird_joy_device), GFP_KERNEL);
+ if (!joy)
+ return -ENOMEM;
+
+ dev = hidinput->input;
+
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ error = input_ff_create_memless(dev, joy, hid_gembird_joy_play);
+ if (error) {
+ kfree(joy);
+ return error;
+ }
+
+ joy->report = report;
+ joy->report->field[0]->value[0] = 0x00;
+ joy->report->field[0]->value[1] = 0x00;
+ joy->report->field[0]->value[2] = 0x00;
+ hid_hw_request(hid, joy->report, HID_REQ_SET_REPORT);
+ }
+
+ hid_info(hid, "Force Feedback for Gembird Joystick devices by Harald Judt <h.judt@gmx.at>\n");
+
+ return 0;
+}
+
+static int gembird_joy_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ return ret;
+ }
+
+ gembird_joy_init(hdev);
+
+ return 0;
+}
+
+static const struct hid_device_id gembird_joy_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD_JOY, USB_DEVICE_ID_GEMBIRD_JOY_SL_6630) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, gembird_joy_devices);
+
+static struct hid_driver gembird_joy_driver = {
+ .name = "gembird_joy",
+ .id_table = gembird_joy_devices,
+ .probe = gembird_joy_probe,
+};
+module_hid_driver(gembird_joy_driver);
+
+MODULE_AUTHOR("Harald Judt <h.judt@gmx.at>");
+MODULE_DESCRIPTION("Force feedback support for HID Gembird joysticks");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index c4589075a5ed..1bdd9b879152 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -528,6 +528,9 @@
#define USB_VENDOR_ID_GEMBIRD 0x11ff
#define USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2 0x3331
+#define USB_VENDOR_ID_GEMBIRD_JOY 0x12bd
+#define USB_DEVICE_ID_GEMBIRD_JOY_SL_6630 0xa02f
+
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 6a8a7ca3d804..835070491da7 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -444,6 +444,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_GEMBIRD)
{ HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD, USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) },
#endif
+#if IS_ENABLED(CONFIG_HID_GEMBIRD_JOY_FF)
+ { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD_JOY, USB_DEVICE_ID_GEMBIRD_JOY_SL_6630) },
+#endif
#if IS_ENABLED(CONFIG_HID_GFRM)
{ HID_BLUETOOTH_DEVICE(0x58, 0x2000) },
{ HID_BLUETOOTH_DEVICE(0x471, 0x2210) },
--
2.52.0
--
`Experience is the best teacher.'
PGP Key ID: 4FFFAB21B8580ABD
Fingerprint: E073 6DD8 FF40 9CF2 0665 11D4 4FFF AB21 B858 0ABD
^ permalink raw reply related
* [dtor-input:next] BUILD SUCCESS 7b9b6b34a6474e6f0e4e7c48e33e824131ed46d5
From: kernel test robot @ 2026-05-08 14:22 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
branch HEAD: 7b9b6b34a6474e6f0e4e7c48e33e824131ed46d5 Input: tsc2007 - reduce I2C transactions for Z2 read
elapsed time: 776m
configs tested: 211
configs skipped: 7
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc haps_hs_smp_defconfig gcc-15.2.0
arc nsimosci_hs_smp_defconfig gcc-15.2.0
arc randconfig-001 gcc-8.5.0
arc randconfig-001-20260508 gcc-12.5.0
arc randconfig-001-20260508 gcc-8.5.0
arc randconfig-002 gcc-8.5.0
arc randconfig-002-20260508 gcc-12.5.0
arc randconfig-002-20260508 gcc-8.5.0
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm h3600_defconfig gcc-15.2.0
arm neponset_defconfig gcc-15.2.0
arm randconfig-001 gcc-8.5.0
arm randconfig-001-20260508 gcc-12.5.0
arm randconfig-001-20260508 gcc-8.5.0
arm randconfig-002 gcc-8.5.0
arm randconfig-002-20260508 gcc-12.5.0
arm randconfig-002-20260508 gcc-8.5.0
arm randconfig-003 gcc-8.5.0
arm randconfig-003-20260508 gcc-12.5.0
arm randconfig-003-20260508 gcc-8.5.0
arm randconfig-004 gcc-8.5.0
arm randconfig-004-20260508 gcc-12.5.0
arm randconfig-004-20260508 gcc-8.5.0
arm64 allmodconfig clang-19
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260508 gcc-14.3.0
arm64 randconfig-002-20260508 gcc-14.3.0
arm64 randconfig-003-20260508 gcc-14.3.0
arm64 randconfig-004-20260508 gcc-14.3.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260508 gcc-14.3.0
csky randconfig-002-20260508 gcc-14.3.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260508 clang-23
hexagon randconfig-002-20260508 clang-23
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001 gcc-14
i386 buildonly-randconfig-001-20260508 gcc-14
i386 buildonly-randconfig-002 gcc-14
i386 buildonly-randconfig-002-20260508 gcc-14
i386 buildonly-randconfig-003 gcc-14
i386 buildonly-randconfig-003-20260508 gcc-14
i386 buildonly-randconfig-004 gcc-14
i386 buildonly-randconfig-004-20260508 gcc-14
i386 buildonly-randconfig-005 gcc-14
i386 buildonly-randconfig-005-20260508 gcc-14
i386 buildonly-randconfig-006 gcc-14
i386 buildonly-randconfig-006-20260508 gcc-14
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260508 gcc-14
i386 randconfig-002-20260508 gcc-14
i386 randconfig-003-20260508 gcc-14
i386 randconfig-004-20260508 gcc-14
i386 randconfig-005-20260508 gcc-14
i386 randconfig-006-20260508 gcc-14
i386 randconfig-007-20260508 gcc-14
i386 randconfig-011-20260508 gcc-13
i386 randconfig-012-20260508 gcc-13
i386 randconfig-013-20260508 gcc-13
i386 randconfig-014-20260508 gcc-13
i386 randconfig-015-20260508 gcc-13
i386 randconfig-016-20260508 gcc-13
i386 randconfig-017-20260508 gcc-13
loongarch allmodconfig clang-19
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260508 clang-23
loongarch randconfig-002-20260508 clang-23
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips loongson3_defconfig gcc-15.2.0
mips omega2p_defconfig clang-23
mips rt305x_defconfig clang-23
nios2 allmodconfig clang-23
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 randconfig-001-20260508 clang-23
nios2 randconfig-002-20260508 clang-23
openrisc allmodconfig clang-23
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-23
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260508 gcc-9.5.0
parisc randconfig-002-20260508 gcc-9.5.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc holly_defconfig clang-23
powerpc randconfig-001-20260508 gcc-9.5.0
powerpc randconfig-002-20260508 gcc-9.5.0
powerpc tqm8555_defconfig gcc-15.2.0
powerpc64 randconfig-001-20260508 gcc-9.5.0
powerpc64 randconfig-002-20260508 gcc-9.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260508 clang-23
riscv randconfig-002-20260508 clang-23
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260508 clang-23
s390 randconfig-002-20260508 clang-23
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh polaris_defconfig gcc-15.2.0
sh randconfig-001-20260508 clang-23
sh randconfig-002-20260508 clang-23
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260508 gcc-12
sparc randconfig-002-20260508 gcc-12
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260508 gcc-12
sparc64 randconfig-002-20260508 gcc-12
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260508 gcc-12
um randconfig-002-20260508 gcc-12
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001 gcc-14
x86_64 buildonly-randconfig-001-20260508 gcc-14
x86_64 buildonly-randconfig-002 gcc-14
x86_64 buildonly-randconfig-002-20260508 gcc-14
x86_64 buildonly-randconfig-003 gcc-14
x86_64 buildonly-randconfig-003-20260508 gcc-14
x86_64 buildonly-randconfig-004 gcc-14
x86_64 buildonly-randconfig-004-20260508 gcc-14
x86_64 buildonly-randconfig-005 gcc-14
x86_64 buildonly-randconfig-005-20260508 gcc-14
x86_64 buildonly-randconfig-006 gcc-14
x86_64 buildonly-randconfig-006-20260508 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260508 clang-20
x86_64 randconfig-002-20260508 clang-20
x86_64 randconfig-003-20260508 clang-20
x86_64 randconfig-004-20260508 clang-20
x86_64 randconfig-005-20260508 clang-20
x86_64 randconfig-006-20260508 clang-20
x86_64 randconfig-011-20260508 gcc-14
x86_64 randconfig-012-20260508 gcc-14
x86_64 randconfig-013-20260508 gcc-14
x86_64 randconfig-014-20260508 gcc-14
x86_64 randconfig-015-20260508 gcc-14
x86_64 randconfig-016-20260508 gcc-14
x86_64 randconfig-071-20260508 gcc-14
x86_64 randconfig-072-20260508 gcc-14
x86_64 randconfig-073-20260508 gcc-14
x86_64 randconfig-074-20260508 gcc-14
x86_64 randconfig-075-20260508 gcc-14
x86_64 randconfig-076-20260508 gcc-14
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa allyesconfig gcc-15.2.0
xtensa randconfig-001-20260508 gcc-12
xtensa randconfig-002-20260508 gcc-12
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v4 2/2] Input: isa1200 - new driver for Imagis ISA1200
From: Dmitry Torokhov @ 2026-05-08 13:57 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
linux-input, devicetree, linux-kernel
In-Reply-To: <CAPVz0n1wYMsLLzbQ=4HLT1+Cx8GoxY+n9Q0cJ=dYv9bF=piOVg@mail.gmail.com>
On Fri, May 08, 2026 at 02:13:43PM +0300, Svyatoslav Ryhel wrote:
> чт, 7 трав. 2026 р. о 22:26 Dmitry Torokhov <dmitry.torokhov@gmail.com> пише:
> >
> > Hi Svyatoslav,
> >
> > On Thu, May 07, 2026 at 04:39:48PM +0300, Svyatoslav Ryhel wrote:
...
> > > +}
> > > +
> > > +static int isa1200_suspend(struct device *dev)
> > > +{
> > > + struct isa1200 *isa = dev_get_drvdata(dev);
> > > +
> > > + cancel_work_sync(&isa->play_work);
> >
> > Move it under input_device_enabled().
> >
> > > +
> > > + guard(mutex)(&isa->input->mutex);
> > > +
>
> Should I remove mutex locking from both PM functions?
No, you should remove it from isa1200_play_work().
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH RFC 3/4] Input: gpio-keys - add regulator to gpio_keys
From: Mark Brown @ 2026-05-08 13:44 UTC (permalink / raw)
To: Griffin Kroah-Hartman
Cc: Dmitry Torokhov, Liam Girdwood, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luca Weiss,
linux-input, linux-kernel, linux-arm-msm, devicetree
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-3-0bb32e8e6428@fairphone.com>
[-- Attachment #1: Type: text/plain, Size: 892 bytes --]
On Fri, May 08, 2026 at 02:53:15PM +0200, Griffin Kroah-Hartman wrote:
> + if (fwnode_property_present(child, "vdd-supply")) {
> + button->regulator = devm_fwnode_regulator_get_optional(dev, child, "vdd");
> + if (IS_ERR(button->regulator)) {
As well as the issue I mentioned on a prior thread with this assigning a
non-physical "vdd" name to the single supply that these components can
have (which has had issues in the past, AHCI being one of the more
painful) the fact that this is fwnode means that this opens up support
for using this with ACPI which is very problematic given that ACPI has a
strong model of how regulators should work which is that they should not
be OS visible at all. That probably needs more addressing in the prior
regulator patch, that needs a bit more motivation and discussion about
the issues with trying to do a regulator interface firmware neutrally.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH] HID: pxrc: fix slab-out-of-bounds read/write in pxrc_raw_event()
From: Jinmo Yang @ 2026-05-08 13:33 UTC (permalink / raw)
To: marcus.folkesson, jikos, benjamin.tissoires
Cc: linux-input, linux-kernel, stable, Jinmo Yang
pxrc_raw_event() accesses data[7] without verifying that the buffer is
large enough. A device that sends a report shorter than 8 bytes causes
an out-of-bounds read (priv->dial = data[7]) and an out-of-bounds write
(data[7] = priv->dial) on the report buffer, corrupting adjacent slab
memory.
This can be triggered from userspace via /dev/uhid by creating a virtual
device with VID 0x1781 / PID 0x0898 and sending a short UHID_INPUT2
report.
Add a size check at the top of pxrc_raw_event() to bail out when the
report buffer is shorter than 8 bytes.
Fixes: a2dccedac664 ("HID: pxrc: new driver for PhoenixRC Flight Controller Adapter")
Cc: stable@vger.kernel.org
Signed-off-by: Jinmo Yang <jinmo44.yang@gmail.com>
---
drivers/hid/hid-pxrc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/hid/hid-pxrc.c b/drivers/hid/hid-pxrc.c
index 07e20ff6018e..f2524547c7a1 100644
--- a/drivers/hid/hid-pxrc.c
+++ b/drivers/hid/hid-pxrc.c
@@ -53,6 +53,9 @@ static int pxrc_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct pxrc_priv *priv = hid_get_drvdata(hdev);
+
+ if (size < 8)
+ return 0;
if (priv->alternate)
priv->slider = data[7];
--
2.47.0
^ permalink raw reply related
* Re: [PATCH RFC 1/4] regulator: add devm_fwnode family of functions
From: Mark Brown @ 2026-05-08 13:31 UTC (permalink / raw)
To: Griffin Kroah-Hartman
Cc: Dmitry Torokhov, Liam Girdwood, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luca Weiss,
linux-input, linux-kernel, linux-arm-msm, devicetree
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-1-0bb32e8e6428@fairphone.com>
[-- Attachment #1: Type: text/plain, Size: 235 bytes --]
On Fri, May 08, 2026 at 02:53:13PM +0200, Griffin Kroah-Hartman wrote:
> Add devm_fwnode_regulator_get and variants.
>
> These function wrappers allow regulators to be accessed from the fwnode
> struct without any casts.
Why?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [RFC PATCH] HID: mcp2221: bound I2C readback copies to the receive buffer
From: gregkh @ 2026-05-08 13:03 UTC (permalink / raw)
To: nsa@linux.do
Cc: gupt21@gmail.com, jikos@kernel.org, bentiss@kernel.org,
linux-input@vger.kernel.org
In-Reply-To: <KL1PR02MB6800676A9E8DCB6454F59B6DF73D2@KL1PR02MB6800.apcprd02.prod.outlook.com>
On Fri, May 08, 2026 at 12:45:44PM +0000, nsa@linux.do wrote:
> The MCP2221 I2C readback path copies data[3] bytes from the incoming HID
> report into mcp->rxbuf at mcp->rxbuf_idx:
>
> memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
>
> The current checks only reject a NULL rxbuf, a negative rxbuf_idx, and
> chunks larger than 60 bytes. They do not verify that the reported chunk
> still fits in the destination buffer for the current transaction.
>
> As a result, a device response can claim a chunk length that exceeds the
> remaining space in the caller-provided receive buffer. For example, a
> 1-byte read request may still return data[3] == 60, which would cause a
> write past the end of the target buffer in the unpatched code.
>
> Track the receive buffer length for the active transaction and reject
> MCP2221_I2C_GET_DATA responses whose chunk would exceed the remaining
> space.bang
>
> This seems security-relevant to me, similar in kind to CVE-2022-50131,
> although it affects a different path.
>
> I also validated this change on a Raspberry Pi system with an actual
> MCP2221 device attached, where the oversized readback chunks were rejected
> and the original out-of-bounds write path was no longer reached.
>
> I am primarily approaching this from the vulnerability research side, so I
> would appreciate maintainer review on the overall fix quality and whether
> this is the preferred way to address it.
>
> Signed-off-by: Lexpl0it<nsa@linux.do>bi
Hi,
This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him
a patch that has triggered this response. He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created. Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.
You are receiving this message because of the following common error(s)
as indicated below:
- Your patch is malformed (tabs converted to spaces, linewrapped, etc.)
and can not be applied. Please read the file,
Documentation/process/email-clients.rst in order to fix this.
- It looks like you did not use your "real" name for the patch on either
the Signed-off-by: line, or the From: line (both of which have to
match). Please read the kernel file,
Documentation/process/submitting-patches.rst for how to do this
correctly.
If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.
thanks,
greg k-h's patch email bot
^ permalink raw reply
* [PATCH RFC 4/4] arm64: dts: qcom: milos-fairphone-fp6: add supply for Hall Effect sensor
From: Griffin Kroah-Hartman @ 2026-05-08 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Liam Girdwood, Mark Brown, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Luca Weiss
Cc: linux-input, linux-kernel, linux-arm-msm, devicetree,
Griffin Kroah-Hartman
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com>
Add vdd-supply for the Hall Effect sensor in gpio-keys so that power for
the sensor will be enabled when it's in use.
With this, we can drop the regulator-always-on for vreg_l10b.
Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
index c1899db46e714137d7849b3b043062fe8b05cc42..ae6900c3f75c64ea5b4feadf38df101abb43c1ea 100644
--- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
+++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts
@@ -32,13 +32,13 @@ gpio-keys {
pinctrl-0 = <&volume_up_default>, <&hall_sensor_default>;
pinctrl-names = "default";
- /* Powered by the always-on vreg_l10b */
event-hall-sensor {
label = "Hall Effect Sensor";
gpios = <&tlmm 70 GPIO_ACTIVE_LOW>;
linux,input-type = <EV_SW>;
linux,code = <SW_LID>;
linux,can-disable;
+ vdd-supply = <&vreg_l10b>;
wakeup-source;
};
@@ -326,8 +326,6 @@ vreg_l10b: ldo10 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
- /* Hall sensor VDD */
- regulator-always-on;
};
vreg_l11b: ldo11 {
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 3/4] Input: gpio-keys - add regulator to gpio_keys
From: Griffin Kroah-Hartman @ 2026-05-08 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Liam Girdwood, Mark Brown, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Luca Weiss
Cc: linux-input, linux-kernel, linux-arm-msm, devicetree,
Griffin Kroah-Hartman
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com>
Allow gpio-keys to have vdd power suppy through regulators.
Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
drivers/input/keyboard/gpio_keys.c | 41 ++++++++++++++++++++++++++++++++++++++
include/linux/gpio_keys.h | 2 ++
2 files changed, 43 insertions(+)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index e196174856796b391f14c31da9b2fee5ff742172..2199ce7455cfc763569e173d4d18da92507f38d3 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
+#include <linux/regulator/consumer.h>
#include <dt-bindings/input/gpio-keys.h>
struct gpio_button_data {
@@ -729,6 +730,7 @@ static int gpio_keys_open(struct input_dev *input)
struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
const struct gpio_keys_platform_data *pdata = ddata->pdata;
int error;
+ int i;
if (pdata->enable) {
error = pdata->enable(input->dev.parent);
@@ -736,19 +738,48 @@ static int gpio_keys_open(struct input_dev *input)
return error;
}
+ for (i = 0; i < pdata->nbuttons; i++) {
+ const struct gpio_keys_button *button = &pdata->buttons[i];
+
+ if (!button->regulator)
+ continue;
+ error = regulator_enable(button->regulator);
+ if (error)
+ goto reg_err;
+ }
+
/* Report current state of buttons that are connected to GPIOs */
gpio_keys_report_state(ddata);
return 0;
+
+reg_err:
+ for (--i; i >= 0; i--) {
+ const struct gpio_keys_button *button = &pdata->buttons[i];
+
+ if (!button->regulator)
+ continue;
+ regulator_disable(button->regulator);
+ }
+ return error;
}
static void gpio_keys_close(struct input_dev *input)
{
struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
const struct gpio_keys_platform_data *pdata = ddata->pdata;
+ int i;
if (pdata->disable)
pdata->disable(input->dev.parent);
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ const struct gpio_keys_button *button = &pdata->buttons[i];
+
+ if (!button->regulator)
+ continue;
+ regulator_disable(button->regulator);
+ }
}
/*
@@ -829,6 +860,16 @@ gpio_keys_get_devtree_pdata(struct device *dev)
&button->debounce_interval))
button->debounce_interval = 5;
+ if (fwnode_property_present(child, "vdd-supply")) {
+ button->regulator = devm_fwnode_regulator_get_optional(dev, child, "vdd");
+ if (IS_ERR(button->regulator)) {
+ if (PTR_ERR(button->regulator) != -ENODEV)
+ return dev_err_ptr_probe(dev, PTR_ERR(button->regulator),
+ "Failed to get regulator\n");
+ button->regulator = NULL;
+ }
+ }
+
button++;
}
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 80fa930b04c6795eb7c6143a79655a6f918446eb..66e786ad7eba407dbea2545941f5b6e464a4f61c 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -3,6 +3,7 @@
#define _GPIO_KEYS_H
#include <linux/types.h>
+#include <linux/regulator/consumer.h>
struct device;
@@ -36,6 +37,7 @@ struct gpio_keys_button {
int value;
unsigned int irq;
unsigned int wakeirq;
+ struct regulator *regulator;
};
/**
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 2/4] dt-bindings: input: gpio-keys: Add vdd-supply
From: Griffin Kroah-Hartman @ 2026-05-08 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Liam Girdwood, Mark Brown, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Luca Weiss
Cc: linux-input, linux-kernel, linux-arm-msm, devicetree,
Griffin Kroah-Hartman
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com>
Allow the use of vdd-supply regulators for gpio-keys. For example, the
Fairphone Gen. 6 Hall effect sensor requires this functionality.
Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
Documentation/devicetree/bindings/input/gpio-keys.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/gpio-keys.yaml b/Documentation/devicetree/bindings/input/gpio-keys.yaml
index cc78c2152921308fe0cad3e29ca78a5fad08f066..9c5ec1e7d0fc7ea236c161c7bd5a041aafdd07a1 100644
--- a/Documentation/devicetree/bindings/input/gpio-keys.yaml
+++ b/Documentation/devicetree/bindings/input/gpio-keys.yaml
@@ -102,6 +102,10 @@ patternProperties:
which can be disabled to suppress events from the button.
type: boolean
+ vdd-supply:
+ description:
+ Regulator that provides a VDD power supply.
+
required:
- linux,code
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 1/4] regulator: add devm_fwnode family of functions
From: Griffin Kroah-Hartman @ 2026-05-08 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Liam Girdwood, Mark Brown, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Luca Weiss
Cc: linux-input, linux-kernel, linux-arm-msm, devicetree,
Griffin Kroah-Hartman
In-Reply-To: <20260508-gpiokeys-vdd-supply-v1-0-0bb32e8e6428@fairphone.com>
Add devm_fwnode_regulator_get and variants.
These function wrappers allow regulators to be accessed from the fwnode
struct without any casts.
Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
drivers/regulator/devres.c | 66 ++++++++++++++++++++++++++++++++++++++
include/linux/regulator/consumer.h | 30 +++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 615deba5d22c8ce190c69081c94651d8df93d002..f525b1f48bcf18593798f6fa520ccdfc656f6b6c 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -11,6 +11,7 @@
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
#include <linux/module.h>
+#include <linux/of.h>
#include "internal.h"
@@ -803,4 +804,69 @@ struct regulator *devm_of_regulator_get_optional(struct device *dev, struct devi
return _devm_of_regulator_get(dev, node, id, OPTIONAL_GET);
}
EXPORT_SYMBOL_GPL(devm_of_regulator_get_optional);
+
+static struct regulator *_devm_fwnode_regulator_get(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id,
+ enum regulator_get_type get_type)
+{
+ if (is_of_node(fwnode))
+ return _devm_of_regulator_get(dev, to_of_node(fwnode), id, get_type);
+
+ return ERR_PTR(-ENODEV);
+}
+
+/**
+ * devm_fwnode_regulator_get() - get resource managed regulator from firmware node
+ * @dev: device to supply
+ * @fwnode: firmware node to get regulator from
+ * @id: supply name or regulator ID.
+ *
+ * Managed of_regulator_get(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * of_regulator_get() for more information.
+ */
+struct regulator *devm_fwnode_regulator_get(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return _devm_fwnode_regulator_get(dev, fwnode, id, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_fwnode_regulator_get);
+
+/**
+ * devm_fwnode_regulator_get_exclusive() - get resource managed regulator from firmware node
+ * @dev: device to supply
+ * @fwnode: firmware node to get regulator from
+ * @id: supply name or regulator ID.
+ *
+ * Managed of_regulator_get_exclusive(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * of_regulator_get_exclusive() for more information.
+ */
+struct regulator *devm_fwnode_regulator_get_exclusive(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return _devm_fwnode_regulator_get(dev, fwnode, id, EXCLUSIVE_GET);
+}
+EXPORT_SYMBOL_GPL(devm_fwnode_regulator_get_exclusive);
+
+/**
+ * devm_fwnode_regulator_get_optional() - get resource managed regulator from firmware node
+ * @dev: device to supply
+ * @fwnode: firmware node to get regulator from
+ * @id: supply name or regulator ID.
+ *
+ * Managed of_regulator_get_optional(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * of_regulator_get_optional() for more information.
+ */
+struct regulator *devm_fwnode_regulator_get_optional(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return _devm_fwnode_regulator_get(dev, fwnode, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_fwnode_regulator_get_optional);
#endif
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index 56fe2693d9b2284d04ebae50165f9aa7b1b3fee4..7c3aedf7f4902c4e3e8e2f5ecf6e4323b77658f3 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -691,6 +691,15 @@ struct regulator *__must_check devm_of_regulator_get_optional(struct device *dev
const char *id);
int __must_check of_regulator_bulk_get_all(struct device *dev, struct device_node *np,
struct regulator_bulk_data **consumers);
+struct regulator *__must_check devm_fwnode_regulator_get(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id);
+struct regulator *__must_check devm_fwnode_regulator_get_exclusive(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id);
+struct regulator *__must_check devm_fwnode_regulator_get_optional(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *id);
#else
static inline struct regulator *__must_check of_regulator_get_optional(struct device *dev,
struct device_node *node,
@@ -712,6 +721,27 @@ static inline int of_regulator_bulk_get_all(struct device *dev, struct device_no
return 0;
}
+static inline struct regulator *__must_check
+devm_fwnode_regulator_get(struct device *dev, struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return NULL;
+}
+
+static inline struct regulator *__must_check
+devm_fwnode_regulator_get_exclusive(struct device *dev, struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return NULL;
+}
+
+static inline struct regulator *__must_check
+devm_fwnode_regulator_get_optional(struct device *dev, struct fwnode_handle *fwnode,
+ const char *id)
+{
+ return NULL;
+}
+
#endif
static inline int regulator_set_voltage_triplet(struct regulator *regulator,
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 0/4] Add regulator support to gpio-keys
From: Griffin Kroah-Hartman @ 2026-05-08 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Liam Girdwood, Mark Brown, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Luca Weiss
Cc: linux-input, linux-kernel, linux-arm-msm, devicetree,
Griffin Kroah-Hartman
This patchset adds regulator support to gpio-keys to ensure that the
regulator powering the Hall Effect sensor IC ("flip cover sensor") is on
when the input device is used.
Marked as RFC because we may want to represent a Hall Effect sensor with
a separate compatible + driver, and not use gpio-keys.
There was also a comment around the usage of of_regulator_get() in the
follow thread:
https://lore.kernel.org/all/DI5HOWHCCKKD.1SQFAA3L4QFDI@fairphone.com/
Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
Griffin Kroah-Hartman (4):
regulator: add devm_fwnode family of functions
dt-bindings: input: gpio-keys: Add vdd-supply
Input: gpio-keys - add regulator to gpio_keys
arm64: dts: qcom: milos-fairphone-fp6: add supply for Hall Effect sensor
.../devicetree/bindings/input/gpio-keys.yaml | 4 ++
arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts | 4 +-
drivers/input/keyboard/gpio_keys.c | 41 ++++++++++++++
drivers/regulator/devres.c | 66 ++++++++++++++++++++++
include/linux/gpio_keys.h | 2 +
include/linux/regulator/consumer.h | 30 ++++++++++
6 files changed, 144 insertions(+), 3 deletions(-)
---
base-commit: 17c7841d09ee7d33557fd075562d9289b6018c90
change-id: 20260410-gpiokeys-vdd-supply-855d6ca8534b
Best regards,
--
Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
^ permalink raw reply
* Re: [PATCH v4 2/2] Input: isa1200 - new driver for Imagis ISA1200
From: Svyatoslav Ryhel @ 2026-05-08 11:13 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
linux-input, devicetree, linux-kernel
In-Reply-To: <afzJUcvQth6v9KGC@google.com>
чт, 7 трав. 2026 р. о 22:26 Dmitry Torokhov <dmitry.torokhov@gmail.com> пише:
>
> Hi Svyatoslav,
>
> On Thu, May 07, 2026 at 04:39:48PM +0300, Svyatoslav Ryhel wrote:
> > From: Linus Walleij <linusw@kernel.org>
> >
> > The ISA1200 is a haptic feedback unit from Imagis Technology using two
> > motors for haptic feedback in mobile phones. Used in many mobile devices
> > c. 2012 including Samsung Galxy S Advance GT-I9070 (Janice), Samsung Beam
> > GT-I8350 (Gavini), LG Optimus 4X P880 and LG Optimus Vu P895.
> >
> > The exact datasheet for the ISA1200 is not available; all data was modeled
> > based on available downstream kernel sources for various devices and
> > fragments of information scattered across the internet.
> >
> > Tested-by: Linus Walleij <linusw@kernel.org> # GT-I9070 Janice
> > Signed-off-by: Linus Walleij <linusw@kernel.org>
> > Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> > drivers/input/misc/Kconfig | 12 +
> > drivers/input/misc/Makefile | 1 +
> > drivers/input/misc/isa1200.c | 540 +++++++++++++++++++++++++++++++++++
> > 3 files changed, 553 insertions(+)
> > create mode 100644 drivers/input/misc/isa1200.c
> >
> > diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> > index 94a753fcb64f..52f192104ee2 100644
> > --- a/drivers/input/misc/Kconfig
> > +++ b/drivers/input/misc/Kconfig
> > @@ -852,6 +852,18 @@ config INPUT_IQS7222
> > To compile this driver as a module, choose M here: the
> > module will be called iqs7222.
> >
> > +config INPUT_ISA1200_HAPTIC
> > + tristate "Imagis ISA1200 haptic feedback unit"
> > + depends on I2C
> > + select INPUT_FF_MEMLESS
> > + select REGMAP_I2C
> > + help
> > + Say Y to enable support for the Imagis ISA1200 haptic
> > + feedback unit.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called isa1200.
> > +
> > config INPUT_CMA3000
> > tristate "VTI CMA3000 Tri-axis accelerometer"
> > help
> > diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> > index 415fc4e2918b..d62bf2e9d85f 100644
> > --- a/drivers/input/misc/Makefile
> > +++ b/drivers/input/misc/Makefile
> > @@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
> > obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
> > obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o
> > obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o
> > +obj-$(CONFIG_INPUT_ISA1200_HAPTIC) += isa1200.o
> > obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
> > obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
> > obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
> > diff --git a/drivers/input/misc/isa1200.c b/drivers/input/misc/isa1200.c
> > new file mode 100644
> > index 000000000000..f8dba8a95c7d
> > --- /dev/null
> > +++ b/drivers/input/misc/isa1200.c
> > @@ -0,0 +1,540 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +
> > +#include <linux/array_size.h>
> > +#include <linux/bitmap.h>
> > +#include <linux/bits.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/devm-helpers.h>
> > +#include <linux/err.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/i2c.h>
> > +#include <linux/input.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/property.h>
> > +#include <linux/pwm.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/units.h>
> > +
> > +/*
> > + * System control (LDO regulator)
> > + *
> > + * LDO voltage to register mapping is linear, but it is split in two parts:
> > + * 2.3V - 3.0V map to 0x08 - 0x0f; 3.1V - 3.8V map to 0x00 - 0x7
> > + */
> > +
> > +#define ISA1200_SCTRL 0x00
> > +#define ISA1200_LDO_VOLTAGE_BASE 0x08
> > +#define ISA1200_LDO_VOLTAGE_STEP 100000
> > +#define ISA1200_LDO_VOLTAGE_2V3 23
> > +#define ISA1200_LDO_VOLTAGE_3V1 31
> > +#define ISA1200_LDO_VOLTAGE_MIN 2300000
> > +#define ISA1200_LDO_VOLTAGE_MAX 3800000
> > +
> > +/*
> > + * The output frequency is calculated with this formula:
> > + *
> > + * base clock frequency
> > + * fout = -----------------------------------------
> > + * (128 - PWM_FREQ) * 2 * PLLDIV * PWM_PERIOD
> > + *
> > + * The base clock frequency is the clock frequency provided on the
> > + * clock input to the chip, divided by the value in HCTRL0
> > + *
> > + * PWM_FREQ is configured in register HCTRL4, it is common to set this
> > + * to 0 to get only two variables to calculate.
> > + *
> > + * PLLDIV is configured in register HCTRL3 (bits 7..4, so 0..15)
> > + * PWM_PERIOD is configured in register HCTRL6
> > + * Further the duty cycle can be configured in HCTRL5
> > + */
> > +
> > +/*
> > + * HCTRL0 configures clock or PWM input and selects the divider for
> > + * the clock input.
> > + */
> > +#define ISA1200_HCTRL0 0x30
> > +#define ISA1200_HCTRL0_HAP_ENABLE BIT(7)
> > +#define ISA1200_HCTRL0_PWM_GEN_MODE BIT(4)
> > +#define ISA1200_HCTRL0_PWM_INPUT_MODE BIT(3)
> > +#define ISA1200_HCTRL0_CLKDIV_128 128
> > +
> > +/*
> > + * HCTRL1 configures the motor type and clock sourse
> > + */
> > +#define ISA1200_HCTRL1 0x31
> > +#define ISA1200_HCTRL1_EXT_CLOCK BIT(7)
> > +#define ISA1200_HCTRL1_DAC_INVERT BIT(6)
> > +#define ISA1200_HCTRL1_MODE(n) (((n) & 1) << 5)
>
> I wonder if this should simply be BIT(5) and you conditionally use it in
> the code. The macro is not really usable to disable the setting...
>
> > +
> > +/* HCTRL2 controls software reset of the chip */
> > +#define ISA1200_HCTRL2 0x32
> > +#define ISA1200_HCTRL2_SW_RESET BIT(0)
> > +
> > +/*
> > + * HCTRL3 controls the PLL divisor
> > + *
> > + * Bits [0,1] are always set to 1 (we don't know what they are
> > + * used for) and bit 4 and upward control the PLL divisor.
> > + */
> > +#define ISA1200_HCTRL3 0x33
> > +#define ISA1200_HCTRL3_DEFAULT 0x03
> > +#define ISA1200_HCTRL3_PLLDIV(n) (((n) & 0xf) << 4)
> > +
> > +/* HCTRL4 controls the PWM frequency of external channel */
> > +#define ISA1200_HCTRL4 0x34
> > +
> > +/* HCTRL5 controls the PWM high duty cycle of internal channel */
> > +#define ISA1200_HCTRL5 0x35
> > +
> > +/* HCTRL6 controls the PWM period of internal channel */
> > +#define ISA1200_HCTRL6 0x36
> > +#define ISA1200_HCTRL6_PERIOD_SCALE 100
> > +
> > +/* The use for these registers is unknown but they exist */
> > +#define ISA1200_HCTRL7 0x37
> > +#define ISA1200_HCTRL8 0x38
> > +#define ISA1200_HCTRL9 0x39
> > +#define ISA1200_HCTRLA 0x3a
> > +#define ISA1200_HCTRLB 0x3b
> > +#define ISA1200_HCTRLC 0x3c
> > +#define ISA1200_HCTRLD 0x3d
> > +
> > +#define ISA1200_EN_PINS_MAX 2
> > +
> > +struct isa1200_config {
> > + u32 ldo_voltage;
> > + u32 mode;
> > + u32 clkdiv;
> > + u32 plldiv;
> > + u32 freq;
> > + u32 period;
> > + u32 duty;
> > +};
> > +
> > +struct isa1200 {
> > + struct input_dev *input;
> > + struct regmap *map;
> > +
> > + struct clk *clk;
> > + struct pwm_device *pwm;
> > + struct gpio_descs *enable_gpios;
> > +
> > + struct work_struct play_work;
> > + struct isa1200_config config;
> > +
> > + int level;
> > + bool clk_on;
>
> I think you need not only clk_on, but general "active" flag that you
> would set at the end of isa1200_start().
>
> > +};
> > +
> > +static const struct regmap_config isa1200_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ISA1200_HCTRLD,
> > +};
> > +
> > +static void isa1200_start(struct isa1200 *isa)
> > +{
> > + struct isa1200_config *config = &isa->config;
> > + struct pwm_state state;
> > + u8 hctrl0 = 0, hctrl1 = 0;
> > + DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
> > + int ret;
>
> Please use "error" or "err" for all variables that only hold error codes
> (or 0) instead of a real value that is used for something.
>
> > +
> > + if (!isa->clk_on) {
> > + ret = clk_prepare_enable(isa->clk);
>
> This return 0 on success so
>
> if (error)
> return;
>
> > + if (ret < 0)
> > + return;
> > +
> > + isa->clk_on = true;
> > + }
> > +
> > + bitmap_fill(values, ISA1200_EN_PINS_MAX);
> > + gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
> > +
> > + usleep_range(200, 300);
> > +
> > + regmap_write(isa->map, ISA1200_SCTRL, config->ldo_voltage);
> > +
> > + if (isa->clk) {
> > + hctrl0 = ISA1200_HCTRL0_PWM_GEN_MODE;
> > + hctrl1 = ISA1200_HCTRL1_EXT_CLOCK;
> > + }
> > +
> > + if (isa->pwm) {
> > + hctrl0 = ISA1200_HCTRL0_PWM_INPUT_MODE;
> > + hctrl1 = 0;
> > + }
> > +
> > + hctrl0 |= __ffs(config->clkdiv / ISA1200_HCTRL0_CLKDIV_128);
> > + hctrl1 |= ISA1200_HCTRL1_DAC_INVERT;
> > + hctrl1 |= ISA1200_HCTRL1_MODE(config->mode);
> > +
> > + regmap_write(isa->map, ISA1200_HCTRL0, hctrl0);
> > + regmap_write(isa->map, ISA1200_HCTRL1, hctrl1);
> > +
> > + /* Make sure to de-assert software reset */
> > + regmap_write(isa->map, ISA1200_HCTRL2, 0x00);
> > +
> > + /* PLL divisor */
> > + regmap_write(isa->map, ISA1200_HCTRL3,
> > + ISA1200_HCTRL3_PLLDIV(config->plldiv) |
> > + ISA1200_HCTRL3_DEFAULT);
> > +
> > + /* Frequency */
> > + regmap_write(isa->map, ISA1200_HCTRL4, config->freq);
> > + /* Duty cycle */
> > + regmap_write(isa->map, ISA1200_HCTRL5, config->period >> 1);
> > + /* Period */
> > + regmap_write(isa->map, ISA1200_HCTRL6, config->period);
> > +
> > + hctrl0 |= ISA1200_HCTRL0_HAP_ENABLE;
> > + regmap_write(isa->map, ISA1200_HCTRL0, hctrl0);
> > +
> > + if (isa->clk)
> > + regmap_write(isa->map, ISA1200_HCTRL5, config->duty);
> > +
> > + if (isa->pwm) {
> > + pwm_get_state(isa->pwm, &state);
> > + state.duty_cycle = config->duty;
> > + state.enabled = true;
> > + pwm_apply_might_sleep(isa->pwm, &state);
> > + }
> > +}
> > +
> > +static void isa1200_power_off(void *data)
> > +{
> > + struct isa1200 *isa = data;
> > +
> > + DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
> > +
> > + bitmap_zero(values, ISA1200_EN_PINS_MAX);
> > + gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
> > +
> > + if (isa->clk_on) {
> > + clk_disable_unprepare(isa->clk);
> > + isa->clk_on = false;
> > + }
> > +}
> > +
> > +static void isa1200_stop(struct isa1200 *isa)
> > +{
> > + struct pwm_state state;
> > +
> > + if (isa->pwm) {
> > + pwm_get_state(isa->pwm, &state);
> > + state.duty_cycle = 0;
> > + state.enabled = false;
> > + pwm_apply_might_sleep(isa->pwm, &state);
> > + }
> > +
> > + regmap_write(isa->map, ISA1200_HCTRL0, 0x00);
> > + isa1200_power_off(isa);
> > +}
> > +
> > +static void isa1200_play_work(struct work_struct *work)
> > +{
> > + struct isa1200 *isa =
> > + container_of(work, struct isa1200, play_work);
> > +
> > + guard(mutex)(&isa->input->mutex);
>
> I think this mutex is dangerous here. You really want to stop/cancel
> work in suspend() and close() instead of blocking on the mutex and then
> continuing.
>
> > +
> > + if (isa->level)
> > + isa1200_start(isa);
> > + else
> > + isa1200_stop(isa);
> > +}
> > +
> > +static int isa1200_vibrator_play_effect(struct input_dev *input, void *data,
> > + struct ff_effect *effect)
> > +{
> > + struct isa1200 *isa = input_get_drvdata(input);
> > + int level;
> > +
> > + /*
> > + * TODO: we currently only support rumble.
> > + * The ISA1200 can control two motors and some devices
> > + * also have two motors mounted.
> > + */
> > + level = effect->u.rumble.strong_magnitude;
> > + if (!level)
> > + level = effect->u.rumble.weak_magnitude;
> > +
> > + dev_dbg(&input->dev, "FF effect type %d level %d\n",
> > + effect->type, level);
> > +
> > + if (isa->level != level) {
> > + isa->level = level;
> > + schedule_work(&isa->play_work);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void isa1200_vibrator_close(struct input_dev *input)
> > +{
> > + struct isa1200 *isa = input_get_drvdata(input);
> > +
> > + cancel_work_sync(&isa->play_work);
> > +
> > + if (isa->level)
>
> So here I think you need to check that "active" flag that would represent
> the committed state.
>
> > + isa1200_stop(isa);
> > +
> > + isa->level = 0;
> > +}
> > +
> > +static int isa1200_of_probe(struct i2c_client *client)
> > +{
> > + static const char * const isa1200_supplies[] = { "vdd", "vddp" };
> > + struct isa1200 *isa = i2c_get_clientdata(client);
> > + struct isa1200_config *config = &isa->config;
> > + struct device *dev = &client->dev;
> > + struct fwnode_handle *ldo_node;
> > + int ret;
> > +
> > + isa->clk = devm_clk_get_optional(dev, NULL);
> > + if (IS_ERR(isa->clk))
> > + return dev_err_probe(dev, PTR_ERR(isa->clk),
> > + "failed to get clock\n");
> > +
> > + isa->pwm = devm_pwm_get(dev, NULL);
> > + if (IS_ERR(isa->pwm)) {
> > + ret = PTR_ERR(isa->pwm);
> > + if (ret == -ENODEV || ret == -EINVAL)
> > + isa->pwm = NULL;
> > + else
> > + return dev_err_probe(dev, ret, "getting PWM\n");
> > + }
> > +
> > + if (!isa->clk && !isa->pwm)
> > + return dev_err_probe(dev, -EINVAL,
> > + "clock or PWM are required, none were provided\n");
> > +
> > + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(isa1200_supplies),
> > + isa1200_supplies);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to set up supplies\n");
> > +
> > + isa->enable_gpios = devm_gpiod_get_array_optional(dev, "control",
> > + GPIOD_OUT_LOW);
> > + if (IS_ERR(isa->enable_gpios))
> > + return dev_err_probe(dev, PTR_ERR(isa->enable_gpios),
> > + "failed to get enable gpios\n");
> > +
> > + ldo_node = device_get_named_child_node(dev, "ldo");
> > + if (!ldo_node)
> > + return dev_err_probe(dev, -ENODEV,
> > + "failed to get embedded LDO node\n");
> > +
> > + ret = fwnode_property_read_u32(ldo_node, "regulator-min-microvolt",
> > + &config->ldo_voltage);
> > + fwnode_handle_put(ldo_node);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "failed to get ldo voltage\n");
> > +
> > + config->ldo_voltage = clamp(config->ldo_voltage,
> > + ISA1200_LDO_VOLTAGE_MIN,
> > + ISA1200_LDO_VOLTAGE_MAX);
> > +
> > + config->ldo_voltage /= ISA1200_LDO_VOLTAGE_STEP;
> > + if (config->ldo_voltage < ISA1200_LDO_VOLTAGE_3V1)
> > + config->ldo_voltage = config->ldo_voltage -
> > + ISA1200_LDO_VOLTAGE_2V3 +
> > + ISA1200_LDO_VOLTAGE_BASE;
> > + else
> > + config->ldo_voltage -= ISA1200_LDO_VOLTAGE_3V1;
> > +
> > + config->mode = 0; /* LRA_MODE */
> > + device_property_read_u32(dev, "imagis,mode", &config->mode);
> > +
> > + config->clkdiv = ISA1200_HCTRL0_CLKDIV_128;
> > + device_property_read_u32(dev, "imagis,clk-div", &config->clkdiv);
> > + if (!config->clkdiv)
> > + return dev_err_probe(dev, -EINVAL, "clk-div cannot be zero\n");
> > +
> > + config->clkdiv = clamp(config->clkdiv, ISA1200_HCTRL0_CLKDIV_128,
> > + ISA1200_HCTRL0_CLKDIV_128 << 3);
> > +
> > + ret = device_property_read_u32(dev, "imagis,pll-div", &config->plldiv);
> > + if (ret < 0 || !config->plldiv)
> > + config->plldiv = 1;
> > +
> > + config->period = 0;
> > + config->freq = 0;
> > + config->duty = 0;
> > +
> > + if (isa->clk) {
> > + ret = device_property_read_u32(dev, "imagis,period-ns",
> > + &config->period);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "failed to get period\n");
> > +
> > + /*
> > + * TODO: The scale value is arbitrary, but it fits observations
> > + * quite well, and the exact conversion method is unknown.
> > + * The period property value returned above is the HCTRL6
> > + * register value set by the vendor code, multiplied by 100.
> > + */
> > + config->period /= ISA1200_HCTRL6_PERIOD_SCALE;
> > + config->duty = config->period >> 1;
> > + }
> > +
> > + if (isa->pwm) {
> > + struct pwm_state state;
> > +
> > + pwm_init_state(isa->pwm, &state);
> > +
> > + if (!state.period)
> > + return dev_err_probe(dev, -EINVAL,
> > + "PWM period cannot be zero\n");
> > +
> > + config->freq = div64_u64(NANO, state.period * config->clkdiv);
> > + config->duty = state.period >> 1;
> > +
> > + ret = pwm_apply_might_sleep(isa->pwm, &state);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "failed to apply initial PWM state\n");
> > + }
> > +
> > + /*
> > + * TODO: If device is using a clock, this property should return the
> > + * value written to the HCTRL5 register by downstrem code. It likely
> > + * needs to be converted into a meaningful duty cycle value, though
> > + * unfortunately the exact conversion mechanism is unknown. If the
> > + * device uses PWM, this property will return the correct duty cycle
> > + * in nanoseconds.
> > + */
> > + device_property_read_u32(dev, "imagis,duty-cycle-ns", &config->duty);
> > +
> > + return 0;
> > +}
> > +
> > +static int isa1200_probe(struct i2c_client *client)
> > +{
> > + struct isa1200 *isa;
> > + struct device *dev = &client->dev;
> > + DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
> > + u32 val;
> > + int ret;
> > +
> > + isa = devm_kzalloc(dev, sizeof(*isa), GFP_KERNEL);
> > + if (!isa)
> > + return -ENOMEM;
> > +
> > + isa->input = devm_input_allocate_device(dev);
> > + if (!isa->input)
> > + return -ENOMEM;
> > +
> > + i2c_set_clientdata(client, isa);
> > +
> > + ret = isa1200_of_probe(client);
> > + if (ret)
> > + return ret;
> > +
> > + isa->map = devm_regmap_init_i2c(client, &isa1200_regmap_config);
> > + if (IS_ERR(isa->map))
> > + return dev_err_probe(dev, PTR_ERR(isa->map),
> > + "failed to initialize register map\n");
> > +
> > + ret = clk_prepare_enable(isa->clk);
> > + if (ret < 0)
> > + return dev_err_probe(dev, ret, "failed to enable clock\n");
> > +
> > + isa->clk_on = true;
> > +
> > + bitmap_fill(values, ISA1200_EN_PINS_MAX);
> > + gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
>
> You should factor our isa1200_power_on() from isa1200_start() and use it
> here.
>
> > +
> > + ret = devm_add_action_or_reset(dev, isa1200_power_off, isa);
> > + if (ret)
> > + return ret;
>
> close() is taking care of powering off the device.
>
> > +
> > + usleep_range(200, 300);
> > +
> > + /* Read a register so we know that regmap and I2C transport works */
> > + ret = regmap_read(isa->map, ISA1200_SCTRL, &val);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to read SCTRL\n");
>
> You should call isa1200_power_off() here. You do not know when the first
> effect will play. In the meantime the device should be in low power
> state.
>
> > +
> > + ret = devm_work_autocancel(dev, &isa->play_work, isa1200_play_work);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to init work\n");
>
> I do not think you need it here. You supply input->close() and that
> should take care of stopping/canceling the work. Input core guarantees
> to call close() if the device is "open" whne unregistering.
>
> > +
> > + isa->input->name = "isa1200-haptic";
> > + isa->input->id.bustype = BUS_HOST;
>
> Not BUS_I2C?
>
> > + isa->input->dev.parent = dev;
>
> Parent is already set by devm_input_allocate_device().
>
> > + isa->input->close = isa1200_vibrator_close;
> > +
> > + input_set_drvdata(isa->input, isa);
> > +
> > + /* TODO: this hardware can likely support more than rumble */
> > + input_set_capability(isa->input, EV_FF, FF_RUMBLE);
> > +
> > + ret = input_ff_create_memless(isa->input, NULL,
> > + isa1200_vibrator_play_effect);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "couldn't create FF dev\n");
>
> "failed ..."
>
> > +
> > + ret = input_register_device(isa->input);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "couldn't register input dev\n");
>
> "failed ..."
>
> > +
> > + return ret;
>
> return 0;
>
> > +}
> > +
> > +static int isa1200_suspend(struct device *dev)
> > +{
> > + struct isa1200 *isa = dev_get_drvdata(dev);
> > +
> > + cancel_work_sync(&isa->play_work);
>
> Move it under input_device_enabled().
>
> > +
> > + guard(mutex)(&isa->input->mutex);
> > +
Should I remove mutex locking from both PM functions?
> > + if (input_device_enabled(isa->input))
> > + if (isa->level)
> > + isa1200_stop(isa);
> > +
> > + return 0;
> > +}
> > +
> > +static int isa1200_resume(struct device *dev)
> > +{
> > + struct isa1200 *isa = dev_get_drvdata(dev);
> > +
> > + guard(mutex)(&isa->input->mutex);
> > +
> > + if (input_device_enabled(isa->input))
> > + if (isa->level)
> > + isa1200_start(isa);
> > +
> > + return 0;
> > +}
> > +
> > +static DEFINE_SIMPLE_DEV_PM_OPS(isa1200_pm_ops, isa1200_suspend, isa1200_resume);
> > +
> > +static const struct of_device_id isa1200_of_match[] = {
> > + { .compatible = "imagis,isa1200" },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, isa1200_of_match);
> > +
> > +static struct i2c_driver isa1200_i2c_driver = {
> > + .driver = {
> > + .name = "isa1200",
> > + .of_match_table = isa1200_of_match,
> > + .pm = pm_sleep_ptr(&isa1200_pm_ops),
> > + },
> > + .probe = isa1200_probe,
> > +};
> > +module_i2c_driver(isa1200_i2c_driver);
> > +
> > +MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");
> > +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
> > +MODULE_DESCRIPTION("Imagis ISA1200 haptic feedback unit");
> > +MODULE_LICENSE("GPL");
>
> Thanks.
>
> --
> Dmitry
^ permalink raw reply
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