diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 31 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 3 | ||||
-rw-r--r-- | drivers/input/misc/ad714x.c | 1 | ||||
-rw-r--r-- | drivers/input/misc/adxl34x.c | 1 | ||||
-rw-r--r-- | drivers/input/misc/aw86927.c | 846 | ||||
-rw-r--r-- | drivers/input/misc/cma3000_d0x.c | 1 | ||||
-rw-r--r-- | drivers/input/misc/max7360-rotary.c | 192 | ||||
-rw-r--r-- | drivers/input/misc/mc13783-pwrbutton.c | 1 | ||||
-rw-r--r-- | drivers/input/misc/pm8941-pwrkey.c | 12 | ||||
-rw-r--r-- | drivers/input/misc/tps6594-pwrbutton.c | 126 | ||||
-rw-r--r-- | drivers/input/misc/uinput.c | 1 |
11 files changed, 1212 insertions, 3 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 0fb21c99a5e3..cc2558630797 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -126,6 +126,17 @@ config INPUT_ATMEL_CAPTOUCH To compile this driver as a module, choose M here: the module will be called atmel_captouch. +config INPUT_AW86927 + tristate "Awinic AW86927 Haptic Driver Support" + depends on I2C && INPUT + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y here if you have an Awinic AW86927 haptic chip. + + To compile this driver as a module, choose M here: the + module will be called aw86927. + config INPUT_BBNSM_PWRKEY tristate "NXP BBNSM Power Key Driver" depends on ARCH_MXC || COMPILE_TEST @@ -230,6 +241,16 @@ config INPUT_M68K_BEEP tristate "M68k Beeper support" depends on M68K +config INPUT_MAX7360_ROTARY + tristate "Maxim MAX7360 Rotary Encoder" + depends on MFD_MAX7360 + help + If you say yes here you get support for the rotary encoder on the + Maxim MAX7360 I/O Expander. + + To compile this driver as a module, choose M here: the module will be + called max7360_rotary. + config INPUT_MAX77650_ONKEY tristate "Maxim MAX77650 ONKEY support" depends on MFD_MAX77650 @@ -506,6 +527,16 @@ config INPUT_TPS65219_PWRBUTTON To compile this driver as a module, choose M here. The module will be called tps65219-pwrbutton. +config INPUT_TPS6594_PWRBUTTON + tristate "TPS6594 Power button driver" + depends on MFD_TPS6594 + help + Say Y here if you want to enable power button reporting for + TPS6594 Power Management IC devices. + + To compile this driver as a module, choose M here. The module will + be called tps6594-pwrbutton. + config INPUT_AXP20X_PEK tristate "X-Powers AXP20X power button driver" depends on MFD_AXP20X diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index d468c8140b93..f5ebfa9d9983 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_INPUT_ATC260X_ONKEY) += atc260x-onkey.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o +obj-$(CONFIG_INPUT_AW86927) += aw86927.o obj-$(CONFIG_INPUT_BBNSM_PWRKEY) += nxp-bbnsm-pwrkey.o obj-$(CONFIG_INPUT_BMA150) += bma150.o obj-$(CONFIG_INPUT_CM109) += cm109.o @@ -51,6 +52,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o @@ -83,6 +85,7 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TPS65219_PWRBUTTON) += tps65219-pwrbutton.o +obj-$(CONFIG_INPUT_TPS6594_PWRBUTTON) += tps6594-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index d106f37df6bc..c9fa789337ba 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -6,6 +6,7 @@ */ #include <linux/device.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 7cafbf8d5f1a..ac7674647c09 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c new file mode 100644 index 000000000000..8ad361239cfe --- /dev/null +++ b/drivers/input/misc/aw86927.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Griffin Kroah-Hartman <griffin.kroah@fairphone.com> + * + * Partially based on vendor driver: + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#define AW86927_RSTCFG_REG 0x00 +#define AW86927_RSTCFG_SOFTRST 0xaa + +#define AW86927_SYSINT_REG 0x02 +#define AW86927_SYSINT_BST_SCPI BIT(7) +#define AW86927_SYSINT_BST_OVPI BIT(6) +#define AW86927_SYSINT_UVLI BIT(5) +#define AW86927_SYSINT_FF_AEI BIT(4) +#define AW86927_SYSINT_FF_AFI BIT(3) +#define AW86927_SYSINT_OCDI BIT(2) +#define AW86927_SYSINT_OTI BIT(1) +#define AW86927_SYSINT_DONEI BIT(0) + +#define AW86927_SYSINTM_REG 0x03 +#define AW86927_SYSINTM_BST_OVPM BIT(6) +#define AW86927_SYSINTM_FF_AEM BIT(4) +#define AW86927_SYSINTM_FF_AFM BIT(3) +#define AW86927_SYSINTM_DONEM BIT(0) + +#define AW86927_PLAYCFG1_REG 0x06 +#define AW86927_PLAYCFG1_BST_MODE_MASK GENMASK(7, 7) +#define AW86927_PLAYCFG1_BST_MODE_BYPASS 0 +#define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(6, 0) +#define AW86927_PLAYCFG1_BST_8500MV 0x50 + +#define AW86927_PLAYCFG2_REG 0x07 + +#define AW86927_PLAYCFG3_REG 0x08 +#define AW86927_PLAYCFG3_AUTO_BST_MASK GENMASK(4, 4) +#define AW86927_PLAYCFG3_AUTO_BST_ENABLE 1 +#define AW86927_PLAYCFG3_AUTO_BST_DISABLE 0 +#define AW86927_PLAYCFG3_PLAY_MODE_MASK GENMASK(1, 0) +#define AW86927_PLAYCFG3_PLAY_MODE_RAM 0 + +#define AW86927_PLAYCFG4_REG 0x09 +#define AW86927_PLAYCFG4_STOP BIT(1) +#define AW86927_PLAYCFG4_GO BIT(0) + +#define AW86927_WAVCFG1_REG 0x0a +#define AW86927_WAVCFG1_WAVSEQ1_MASK GENMASK(6, 0) + +#define AW86927_WAVCFG2_REG 0x0b +#define AW86927_WAVCFG2_WAVSEQ2_MASK GENMASK(6, 0) + +#define AW86927_WAVCFG9_REG 0x12 +#define AW86927_WAVCFG9_SEQ1LOOP_MASK GENMASK(7, 4) +#define AW86927_WAVCFG9_SEQ1LOOP_INFINITELY 0x0f + +#define AW86927_CONTCFG1_REG 0x18 +#define AW86927_CONTCFG1_BRK_BST_MD_MASK GENMASK(6, 6) + +#define AW86927_CONTCFG5_REG 0x1c +#define AW86927_CONTCFG5_BST_BRK_GAIN_MASK GENMASK(7, 4) +#define AW86927_CONTCFG5_BRK_GAIN_MASK GENMASK(3, 0) + +#define AW86927_CONTCFG10_REG 0x21 +#define AW86927_CONTCFG10_BRK_TIME_MASK GENMASK(7, 0) +#define AW86927_CONTCFG10_BRK_TIME_DEFAULT 8 + +#define AW86927_CONTCFG13_REG 0x24 +#define AW86927_CONTCFG13_TSET_MASK GENMASK(7, 4) +#define AW86927_CONTCFG13_BEME_SET_MASK GENMASK(3, 0) + +#define AW86927_BASEADDRH_REG 0x2d +#define AW86927_BASEADDRL_REG 0x2e + +#define AW86927_GLBRD5_REG 0x3f +#define AW86927_GLBRD5_STATE_MASK GENMASK(3, 0) +#define AW86927_GLBRD5_STATE_STANDBY 0 + +#define AW86927_RAMADDRH_REG 0x40 + +#define AW86927_RAMADDRL_REG 0x41 + +#define AW86927_RAMDATA_REG 0x42 + +#define AW86927_SYSCTRL3_REG 0x45 +#define AW86927_SYSCTRL3_STANDBY_MASK GENMASK(5, 5) +#define AW86927_SYSCTRL3_STANDBY_ON 1 +#define AW86927_SYSCTRL3_STANDBY_OFF 0 +#define AW86927_SYSCTRL3_EN_RAMINIT_MASK GENMASK(2, 2) +#define AW86927_SYSCTRL3_EN_RAMINIT_ON 1 +#define AW86927_SYSCTRL3_EN_RAMINIT_OFF 0 + +#define AW86927_SYSCTRL4_REG 0x46 +#define AW86927_SYSCTRL4_WAVDAT_MODE_MASK GENMASK(6, 5) +#define AW86927_SYSCTRL4_WAVDAT_24K 0 +#define AW86927_SYSCTRL4_INT_EDGE_MODE_MASK GENMASK(4, 4) +#define AW86927_SYSCTRL4_INT_EDGE_MODE_POS 0 +#define AW86927_SYSCTRL4_INT_MODE_MASK GENMASK(3, 3) +#define AW86927_SYSCTRL4_INT_MODE_EDGE 1 +#define AW86927_SYSCTRL4_GAIN_BYPASS_MASK GENMASK(0, 0) + +#define AW86927_PWMCFG1_REG 0x48 +#define AW86927_PWMCFG1_PRC_EN_MASK GENMASK(7, 7) +#define AW86927_PWMCFG1_PRC_DISABLE 0 + +#define AW86927_PWMCFG3_REG 0x4a +#define AW86927_PWMCFG3_PR_EN_MASK GENMASK(7, 7) +#define AW86927_PWMCFG3_PRCTIME_MASK GENMASK(6, 0) + +#define AW86927_PWMCFG4_REG 0x4b +#define AW86927_PWMCFG4_PRTIME_MASK GENMASK(7, 0) + +#define AW86927_VBATCTRL_REG 0x4c +#define AW86927_VBATCTRL_VBAT_MODE_MASK GENMASK(6, 6) +#define AW86927_VBATCTRL_VBAT_MODE_SW 0 + +#define AW86927_DETCFG1_REG 0x4d +#define AW86927_DETCFG1_DET_GO_MASK GENMASK(1, 0) +#define AW86927_DETCFG1_DET_GO_DET_SEQ0 1 +#define AW86927_DETCFG1_DET_GO_NA 0 + +#define AW86927_DETCFG2_REG 0x4e +#define AW86927_DETCFG2_DET_SEQ0_MASK GENMASK(6, 3) +#define AW86927_DETCFG2_DET_SEQ0_VBAT 0 +#define AW86927_DETCFG2_D2S_GAIN_MASK GENMASK(2, 0) +#define AW86927_DETCFG2_D2S_GAIN_10 4 + +#define AW86927_CHIPIDH_REG 0x57 +#define AW86927_CHIPIDL_REG 0x58 +#define AW86927_CHIPID 0x9270 + +#define AW86927_TMCFG_REG 0x5b +#define AW86927_TMCFG_UNLOCK 0x7d +#define AW86927_TMCFG_LOCK 0x00 + +#define AW86927_ANACFG11_REG 0x70 + +#define AW86927_ANACFG12_REG 0x71 +#define AW86927_ANACFG12_BST_SKIP_MASK GENMASK(7, 7) +#define AW86927_ANACFG12_BST_SKIP_SHUTDOWN 1 + +#define AW86927_ANACFG13_REG 0x72 +#define AW86927_ANACFG13_BST_PC_MASK GENMASK(7, 4) +#define AW86927_ANACFG13_BST_PEAKCUR_3P45A 6 + +#define AW86927_ANACFG15_REG 0x74 +#define AW86927_ANACFG15_BST_PEAK_MODE_MASK GENMASK(7, 7) +#define AW86927_ANACFG15_BST_PEAK_BACK 1 + +#define AW86927_ANACFG16_REG 0x75 +#define AW86927_ANACFG16_BST_SRC_MASK GENMASK(4, 4) +#define AW86927_ANACFG16_BST_SRC_3NS 0 + +/* default value of base addr */ +#define AW86927_RAM_BASE_ADDR 0x800 +#define AW86927_BASEADDRH_VAL 0x08 +#define AW86927_BASEADDRL_VAL 0x00 + +enum aw86927_work_mode { + AW86927_STANDBY_MODE, + AW86927_RAM_MODE, +}; + +struct aw86927_data { + struct work_struct play_work; + struct device *dev; + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + bool running; +}; + +static const struct regmap_config aw86927_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .max_register = 0x80, +}; + +/* + * Sine wave representing the magnitude of the drive to be used. + * Data is encoded in two's complement. + * round(84 * sin(x / 16.25)) + */ +static const u8 aw86927_waveform[] = { + 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x1a, 0x1f, 0x23, 0x28, 0x2d, 0x31, 0x35, + 0x39, 0x3d, 0x41, 0x44, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4a, 0x47, + 0x44, 0x41, 0x3d, 0x3a, 0x36, 0x31, 0x2d, 0x28, 0x24, 0x1f, 0x1a, 0x15, + 0x10, 0x0a, 0x05, 0x00, 0xfc, 0xf6, 0xf1, 0xec, 0xe7, 0xe2, 0xdd, 0xd8, + 0xd4, 0xcf, 0xcb, 0xc7, 0xc3, 0xbf, 0xbc, 0xb9, 0xb6, 0xb4, 0xb1, 0xb0, + 0xae, 0xad, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, 0xac, 0xae, 0xaf, 0xb1, + 0xb3, 0xb6, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xca, 0xce, 0xd3, 0xd7, 0xdc, + 0xe1, 0xe6, 0xeb, 0xf0, 0xf5, 0xfb +}; + +struct aw86927_sram_waveform_header { + u8 version; + __be16 start_address; + __be16 end_address; +} __packed; + +static const struct aw86927_sram_waveform_header sram_waveform_header = { + .version = 0x01, + .start_address = cpu_to_be16(AW86927_RAM_BASE_ADDR + + sizeof(struct aw86927_sram_waveform_header)), + .end_address = cpu_to_be16(AW86927_RAM_BASE_ADDR + + sizeof(struct aw86927_sram_waveform_header) + + ARRAY_SIZE(aw86927_waveform) - 1), +}; + +static int aw86927_wait_enter_standby(struct aw86927_data *haptics) +{ + unsigned int reg_val; + int err; + + err = regmap_read_poll_timeout(haptics->regmap, AW86927_GLBRD5_REG, reg_val, + (FIELD_GET(AW86927_GLBRD5_STATE_MASK, reg_val) == + AW86927_GLBRD5_STATE_STANDBY), + 2500, 2500 * 100); + + if (err) { + dev_err(haptics->dev, "did not enter standby: %d\n", err); + return err; + } + return 0; +} + +static int aw86927_play_mode(struct aw86927_data *haptics, u8 play_mode) +{ + int err; + + switch (play_mode) { + case AW86927_STANDBY_MODE: + /* Briefly toggle standby, then toggle back to standby off */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_STANDBY_MASK, + FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK, + AW86927_SYSCTRL3_STANDBY_ON)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_STANDBY_MASK, + FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK, + AW86927_SYSCTRL3_STANDBY_OFF)); + if (err) + return err; + + break; + + case AW86927_RAM_MODE: + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_PLAY_MODE_MASK, + FIELD_PREP(AW86927_PLAYCFG3_PLAY_MODE_MASK, + AW86927_PLAYCFG3_PLAY_MODE_RAM)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_MODE_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_MODE_MASK, + AW86927_PLAYCFG1_BST_MODE_BYPASS)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_VBATCTRL_REG, + AW86927_VBATCTRL_VBAT_MODE_MASK, + FIELD_PREP(AW86927_VBATCTRL_VBAT_MODE_MASK, + AW86927_VBATCTRL_VBAT_MODE_SW)); + if (err) + return err; + + break; + } + + return 0; +} + +static int aw86927_stop(struct aw86927_data *haptics) +{ + int err; + + err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_STOP); + if (err) { + dev_err(haptics->dev, "Failed to stop playback: %d\n", err); + return err; + } + + err = aw86927_wait_enter_standby(haptics); + if (err) { + dev_err(haptics->dev, "Failed to enter standby, trying to force it\n"); + err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE); + if (err) + return err; + } + + return 0; +} + +static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct aw86927_data *haptics = input_get_drvdata(dev); + int level; + + level = effect->u.rumble.strong_magnitude; + if (!level) + level = effect->u.rumble.weak_magnitude; + + /* If already running, don't restart playback */ + if (haptics->running && level) + return 0; + + haptics->running = level; + schedule_work(&haptics->play_work); + + return 0; +} + +static int aw86927_play_sine(struct aw86927_data *haptics) +{ + int err; + + err = aw86927_stop(haptics); + if (err) + return err; + + err = aw86927_play_mode(haptics, AW86927_RAM_MODE); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_AUTO_BST_MASK, + FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK, + AW86927_PLAYCFG3_AUTO_BST_ENABLE)); + if (err) + return err; + + /* Set waveseq 1 to the first wave */ + err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG1_REG, + AW86927_WAVCFG1_WAVSEQ1_MASK, + FIELD_PREP(AW86927_WAVCFG1_WAVSEQ1_MASK, 1)); + if (err) + return err; + + /* set wavseq 2 to zero */ + err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG2_REG, + AW86927_WAVCFG2_WAVSEQ2_MASK, + FIELD_PREP(AW86927_WAVCFG2_WAVSEQ2_MASK, 0)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_WAVCFG9_REG, + AW86927_WAVCFG9_SEQ1LOOP_MASK, + FIELD_PREP(AW86927_WAVCFG9_SEQ1LOOP_MASK, + AW86927_WAVCFG9_SEQ1LOOP_INFINITELY)); + if (err) + return err; + + /* set gain to value lower than 0x80 to avoid distorted playback */ + err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, 0x7c); + if (err) + return err; + + /* Start playback */ + err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_GO); + if (err) + return err; + + return 0; +} + +static void aw86927_close(struct input_dev *input) +{ + struct aw86927_data *haptics = input_get_drvdata(input); + struct device *dev = &haptics->client->dev; + int err; + + cancel_work_sync(&haptics->play_work); + + err = aw86927_stop(haptics); + if (err) + dev_err(dev, "Failed to close the Driver: %d\n", err); +} + +static void aw86927_haptics_play_work(struct work_struct *work) +{ + struct aw86927_data *haptics = + container_of(work, struct aw86927_data, play_work); + struct device *dev = &haptics->client->dev; + int err; + + if (haptics->running) + err = aw86927_play_sine(haptics); + else + err = aw86927_stop(haptics); + + if (err) + dev_err(dev, "Failed to execute work command: %d\n", err); +} + +static void aw86927_hw_reset(struct aw86927_data *haptics) +{ + /* Assert reset */ + gpiod_set_value_cansleep(haptics->reset_gpio, 1); + /* Wait ~1ms */ + usleep_range(1000, 2000); + /* Deassert reset */ + gpiod_set_value_cansleep(haptics->reset_gpio, 0); + /* Wait ~8ms until I2C is accessible */ + usleep_range(8000, 8500); +} + +static int aw86927_haptic_init(struct aw86927_data *haptics) +{ + int err; + + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL4_REG, + AW86927_SYSCTRL4_WAVDAT_MODE_MASK, + FIELD_PREP(AW86927_SYSCTRL4_WAVDAT_MODE_MASK, + AW86927_SYSCTRL4_WAVDAT_24K)); + if (err) + return err; + + /* enable gain bypass */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL4_REG, + AW86927_SYSCTRL4_GAIN_BYPASS_MASK, + FIELD_PREP(AW86927_SYSCTRL4_GAIN_BYPASS_MASK, + 0x01)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK); + if (err) + return err; + + err = regmap_write(haptics->regmap, AW86927_ANACFG11_REG, 0x0f); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG12_REG, + AW86927_ANACFG12_BST_SKIP_MASK, + FIELD_PREP(AW86927_ANACFG12_BST_SKIP_MASK, + AW86927_ANACFG12_BST_SKIP_SHUTDOWN)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG15_REG, + AW86927_ANACFG15_BST_PEAK_MODE_MASK, + FIELD_PREP(AW86927_ANACFG15_BST_PEAK_MODE_MASK, + AW86927_ANACFG15_BST_PEAK_BACK)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG16_REG, + AW86927_ANACFG16_BST_SRC_MASK, + FIELD_PREP(AW86927_ANACFG16_BST_SRC_MASK, + AW86927_ANACFG16_BST_SRC_3NS)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_LOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_CONTCFG1_REG, + AW86927_CONTCFG1_BRK_BST_MD_MASK, + FIELD_PREP(AW86927_CONTCFG1_BRK_BST_MD_MASK, 0x00)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_CONTCFG5_REG, + FIELD_PREP(AW86927_CONTCFG5_BST_BRK_GAIN_MASK, 0x05) | + FIELD_PREP(AW86927_CONTCFG5_BRK_GAIN_MASK, 0x08)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, AW86927_CONTCFG10_REG, + AW86927_CONTCFG10_BRK_TIME_MASK, + FIELD_PREP(AW86927_CONTCFG10_BRK_TIME_MASK, + AW86927_CONTCFG10_BRK_TIME_DEFAULT)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_CONTCFG13_REG, + FIELD_PREP(AW86927_CONTCFG13_TSET_MASK, 0x06) | + FIELD_PREP(AW86927_CONTCFG13_BEME_SET_MASK, 0x02)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG2_REG, + AW86927_DETCFG2_D2S_GAIN_MASK, + FIELD_PREP(AW86927_DETCFG2_D2S_GAIN_MASK, + AW86927_DETCFG2_D2S_GAIN_10)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PWMCFG1_REG, + AW86927_PWMCFG1_PRC_EN_MASK, + FIELD_PREP(AW86927_PWMCFG1_PRC_EN_MASK, + AW86927_PWMCFG1_PRC_DISABLE)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_PWMCFG3_REG, + FIELD_PREP(AW86927_PWMCFG3_PR_EN_MASK, 0x01) | + FIELD_PREP(AW86927_PWMCFG3_PRCTIME_MASK, 0x3f)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PWMCFG4_REG, + AW86927_PWMCFG4_PRTIME_MASK, + FIELD_PREP(AW86927_PWMCFG4_PRTIME_MASK, 0x32)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG13_REG, + AW86927_ANACFG13_BST_PC_MASK, + FIELD_PREP(AW86927_ANACFG13_BST_PC_MASK, + AW86927_ANACFG13_BST_PEAKCUR_3P45A)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_LOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86927_PLAYCFG1_BST_8500MV)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_AUTO_BST_MASK, + FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK, + AW86927_PLAYCFG3_AUTO_BST_DISABLE)); + if (err) + return err; + + return 0; +} + +static int aw86927_ram_init(struct aw86927_data *haptics) +{ + int err; + + err = aw86927_wait_enter_standby(haptics); + if (err) + return err; + + /* Enable SRAM init */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_EN_RAMINIT_MASK, + FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, + AW86927_SYSCTRL3_EN_RAMINIT_ON)); + + /* Set base address for the start of the SRAM waveforms */ + err = regmap_write(haptics->regmap, + AW86927_BASEADDRH_REG, AW86927_BASEADDRH_VAL); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_BASEADDRL_REG, AW86927_BASEADDRL_VAL); + if (err) + return err; + + /* Set start of SRAM, before the data is written it will be the same as the base */ + err = regmap_write(haptics->regmap, + AW86927_RAMADDRH_REG, AW86927_BASEADDRH_VAL); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_RAMADDRL_REG, AW86927_BASEADDRL_VAL); + if (err) + return err; + + /* Write waveform header to SRAM */ + err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG, + &sram_waveform_header, sizeof(sram_waveform_header)); + if (err) + return err; + + /* Write waveform to SRAM */ + err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG, + aw86927_waveform, ARRAY_SIZE(aw86927_waveform)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG2_REG, + AW86927_DETCFG2_DET_SEQ0_MASK, + FIELD_PREP(AW86927_DETCFG2_DET_SEQ0_MASK, + AW86927_DETCFG2_DET_SEQ0_VBAT)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG1_REG, + AW86927_DETCFG1_DET_GO_MASK, + FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK, + AW86927_DETCFG1_DET_GO_DET_SEQ0)); + if (err) + return err; + + usleep_range(3000, 3500); + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG1_REG, + AW86927_DETCFG1_DET_GO_MASK, + FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK, + AW86927_DETCFG1_DET_GO_NA)); + if (err) + return err; + + /* Disable SRAM init */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_EN_RAMINIT_MASK, + FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, + AW86927_SYSCTRL3_EN_RAMINIT_OFF)); + if (err) + return err; + + return 0; +} + +static irqreturn_t aw86927_irq(int irq, void *data) +{ + struct aw86927_data *haptics = data; + struct device *dev = &haptics->client->dev; + unsigned int reg_val; + int err; + + err = regmap_read(haptics->regmap, AW86927_SYSINT_REG, ®_val); + if (err) { + dev_err(dev, "Failed to read SYSINT register: %d\n", err); + return IRQ_NONE; + } + + if (reg_val & AW86927_SYSINT_BST_SCPI) + dev_err(dev, "Received a Short Circuit Protection interrupt\n"); + if (reg_val & AW86927_SYSINT_BST_OVPI) + dev_err(dev, "Received an Over Voltage Protection interrupt\n"); + if (reg_val & AW86927_SYSINT_UVLI) + dev_err(dev, "Received an Under Voltage Lock Out interrupt\n"); + if (reg_val & AW86927_SYSINT_OCDI) + dev_err(dev, "Received an Over Current interrupt\n"); + if (reg_val & AW86927_SYSINT_OTI) + dev_err(dev, "Received an Over Temperature interrupt\n"); + + if (reg_val & AW86927_SYSINT_DONEI) + dev_dbg(dev, "Chip playback done!\n"); + if (reg_val & AW86927_SYSINT_FF_AFI) + dev_dbg(dev, "The RTP mode FIFO is almost full!\n"); + if (reg_val & AW86927_SYSINT_FF_AEI) + dev_dbg(dev, "The RTP mode FIFO is almost empty!\n"); + + return IRQ_HANDLED; +} + +static int aw86927_detect(struct aw86927_data *haptics) +{ + __be16 read_buf; + u16 chip_id; + int err; + + err = regmap_bulk_read(haptics->regmap, AW86927_CHIPIDH_REG, &read_buf, 2); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to read CHIPID registers\n"); + + chip_id = be16_to_cpu(read_buf); + + if (chip_id != AW86927_CHIPID) { + dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id); + return -ENODEV; + } + + return 0; +} + +static int aw86927_probe(struct i2c_client *client) +{ + struct aw86927_data *haptics; + int err; + + haptics = devm_kzalloc(&client->dev, sizeof(struct aw86927_data), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->dev = &client->dev; + haptics->client = client; + + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &aw86927_regmap_config); + if (IS_ERR(haptics->regmap)) + return dev_err_probe(haptics->dev, PTR_ERR(haptics->regmap), + "Failed to allocate register map\n"); + + haptics->input_dev = devm_input_allocate_device(haptics->dev); + if (!haptics->input_dev) + return -ENOMEM; + + haptics->reset_gpio = devm_gpiod_get(haptics->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(haptics->reset_gpio)) + return dev_err_probe(haptics->dev, PTR_ERR(haptics->reset_gpio), + "Failed to get reset gpio\n"); + + /* Hardware reset */ + aw86927_hw_reset(haptics); + + /* Software reset */ + err = regmap_write(haptics->regmap, AW86927_RSTCFG_REG, AW86927_RSTCFG_SOFTRST); + if (err) + return dev_err_probe(haptics->dev, err, "Failed Software reset\n"); + + /* Wait ~3ms until I2C is accessible */ + usleep_range(3000, 3500); + + err = aw86927_detect(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to find chip\n"); + + /* IRQ config */ + err = regmap_write(haptics->regmap, AW86927_SYSCTRL4_REG, + FIELD_PREP(AW86927_SYSCTRL4_INT_MODE_MASK, + AW86927_SYSCTRL4_INT_MODE_EDGE) | + FIELD_PREP(AW86927_SYSCTRL4_INT_EDGE_MODE_MASK, + AW86927_SYSCTRL4_INT_EDGE_MODE_POS)); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to configure interrupt modes\n"); + + err = regmap_write(haptics->regmap, AW86927_SYSINTM_REG, + AW86927_SYSINTM_BST_OVPM | + AW86927_SYSINTM_FF_AEM | + AW86927_SYSINTM_FF_AFM | + AW86927_SYSINTM_DONEM); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to configure interrupt masks\n"); + + err = devm_request_threaded_irq(haptics->dev, client->irq, NULL, + aw86927_irq, IRQF_ONESHOT, NULL, haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to request threaded irq\n"); + + INIT_WORK(&haptics->play_work, aw86927_haptics_play_work); + + haptics->input_dev->name = "aw86927-haptics"; + haptics->input_dev->close = aw86927_close; + + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + err = input_ff_create_memless(haptics->input_dev, NULL, aw86927_haptics_play); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to create FF dev\n"); + + /* Set up registers */ + err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE); + if (err) + return dev_err_probe(haptics->dev, err, + "Failed to enter standby for Haptic init\n"); + + err = aw86927_haptic_init(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Haptic init failed\n"); + + /* RAM init, upload the waveform for playback */ + err = aw86927_ram_init(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to init aw86927 sram\n"); + + err = input_register_device(haptics->input_dev); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to register input device\n"); + + return 0; +} + +static const struct of_device_id aw86927_of_id[] = { + { .compatible = "awinic,aw86927" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, aw86927_of_id); + +static struct i2c_driver aw86927_driver = { + .driver = { + .name = "aw86927-haptics", + .of_match_table = aw86927_of_id, + }, + .probe = aw86927_probe, +}; + +module_i2c_driver(aw86927_driver); + +MODULE_AUTHOR("Griffin Kroah-Hartman <griffin.kroah@fairphone.com>"); +MODULE_DESCRIPTION("AWINIC AW86927 LRA Haptic Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c index cfc12332bee1..b4232b0a3957 100644 --- a/drivers/input/misc/cma3000_d0x.c +++ b/drivers/input/misc/cma3000_d0x.c @@ -6,6 +6,7 @@ * Author: Hemanth V <hemanthv@ti.com> */ +#include <linux/export.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/delay.h> diff --git a/drivers/input/misc/max7360-rotary.c b/drivers/input/misc/max7360-rotary.c new file mode 100644 index 000000000000..385831ef34b6 --- /dev/null +++ b/drivers/input/misc/max7360-rotary.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025 Bootlin + * + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> + */ + +#include <linux/bitfield.h> +#include <linux/device/devres.h> +#include <linux/dev_printk.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/max7360.h> +#include <linux/property.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define MAX7360_ROTARY_DEFAULT_STEPS 24 + +struct max7360_rotary { + struct input_dev *input; + struct regmap *regmap; + unsigned int debounce_ms; + + unsigned int pos; + + u32 steps; + u32 axis; + bool relative_axis; + bool rollover; +}; + +static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps) +{ + if (max7360_rotary->relative_axis) { + input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps); + } else { + int pos = max7360_rotary->pos; + int maxval = max7360_rotary->steps; + + /* + * Add steps to the position. + * Make sure added steps are always in ]-maxval; maxval[ + * interval, so (pos + maxval) is always >= 0. + * Then set back pos to the [0; maxval[ interval. + */ + pos += steps % maxval; + if (max7360_rotary->rollover) + pos = (pos + maxval) % maxval; + else + pos = clamp(pos, 0, maxval - 1); + + max7360_rotary->pos = pos; + input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos); + } + + input_sync(max7360_rotary->input); +} + +static irqreturn_t max7360_rotary_irq(int irq, void *data) +{ + struct max7360_rotary *max7360_rotary = data; + struct device *dev = max7360_rotary->input->dev.parent; + unsigned int val; + int error; + + error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val); + if (error < 0) { + dev_err(dev, "Failed to read rotary counter\n"); + return IRQ_NONE; + } + + if (val == 0) + return IRQ_NONE; + + max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7)); + + return IRQ_HANDLED; +} + +static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary) +{ + struct device *dev = max7360_rotary->input->dev.parent; + int val; + int error; + + val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) | + FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY; + error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val); + if (error) + dev_err(dev, "Failed to set max7360 rotary encoder configuration\n"); + + return error; +} + +static int max7360_rotary_probe(struct platform_device *pdev) +{ + struct max7360_rotary *max7360_rotary; + struct device *dev = &pdev->dev; + struct input_dev *input; + struct regmap *regmap; + int irq; + int error; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); + + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); + + max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL); + if (!max7360_rotary) + return -ENOMEM; + + max7360_rotary->regmap = regmap; + + device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis); + max7360_rotary->rollover = device_property_read_bool(dev->parent, + "rotary-encoder,rollover"); + max7360_rotary->relative_axis = + device_property_read_bool(dev->parent, "rotary-encoder,relative-axis"); + + error = device_property_read_u32(dev->parent, "rotary-encoder,steps", + &max7360_rotary->steps); + if (error) + max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS; + + device_property_read_u32(dev->parent, "rotary-debounce-delay-ms", + &max7360_rotary->debounce_ms); + if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX) + return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n", + max7360_rotary->debounce_ms); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + max7360_rotary->input = input; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + + if (max7360_rotary->relative_axis) + input_set_capability(input, EV_REL, max7360_rotary->axis); + else + input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1); + + error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq, + IRQF_ONESHOT | IRQF_SHARED, + "max7360-rotary", max7360_rotary); + if (error) + return dev_err_probe(dev, error, "Failed to register interrupt\n"); + + error = input_register_device(input); + if (error) + return dev_err_probe(dev, error, "Could not register input device\n"); + + error = max7360_rotary_hw_init(max7360_rotary); + if (error) + return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n"); + + device_init_wakeup(dev, true); + error = dev_pm_set_wake_irq(dev, irq); + if (error) + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); + + return 0; +} + +static void max7360_rotary_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); +} + +static struct platform_driver max7360_rotary_driver = { + .driver = { + .name = "max7360-rotary", + }, + .probe = max7360_rotary_probe, + .remove = max7360_rotary_remove, +}; +module_platform_driver(max7360_rotary_driver); + +MODULE_DESCRIPTION("MAX7360 Rotary driver"); +MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 1c7faa9b7afe..b83d762ae2e9 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -57,7 +57,6 @@ static irqreturn_t button_irq(int irq, void *_priv) struct mc13783_pwrb *priv = _priv; int val; - mc13xxx_irq_ack(priv->mc13783, irq); mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); switch (irq) { diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index d952c16f2458..53249d2c081f 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -60,6 +60,7 @@ struct pm8941_data { bool supports_ps_hold_poff_config; bool supports_debounce_config; bool has_pon_pbs; + bool wakeup_source_default; const char *name; const char *phys; }; @@ -245,7 +246,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, static int pm8941_pwrkey_probe(struct platform_device *pdev) { struct pm8941_pwrkey *pwrkey; - bool pull_up; + bool pull_up, wakeup; struct device *parent; struct device_node *regmap_node; const __be32 *addr; @@ -402,8 +403,11 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) } } + wakeup = pwrkey->data->wakeup_source_default || + of_property_read_bool(pdev->dev.of_node, "wakeup-source"); + platform_set_drvdata(pdev, pwrkey); - device_init_wakeup(&pdev->dev, 1); + device_init_wakeup(&pdev->dev, wakeup); return 0; } @@ -424,6 +428,7 @@ static const struct pm8941_data pwrkey_data = { .supports_ps_hold_poff_config = true, .supports_debounce_config = true, .has_pon_pbs = false, + .wakeup_source_default = true, }; static const struct pm8941_data resin_data = { @@ -434,6 +439,7 @@ static const struct pm8941_data resin_data = { .supports_ps_hold_poff_config = true, .supports_debounce_config = true, .has_pon_pbs = false, + .wakeup_source_default = false, }; static const struct pm8941_data pon_gen3_pwrkey_data = { @@ -443,6 +449,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = { .supports_ps_hold_poff_config = false, .supports_debounce_config = false, .has_pon_pbs = true, + .wakeup_source_default = true, }; static const struct pm8941_data pon_gen3_resin_data = { @@ -452,6 +459,7 @@ static const struct pm8941_data pon_gen3_resin_data = { .supports_ps_hold_poff_config = false, .supports_debounce_config = false, .has_pon_pbs = true, + .wakeup_source_default = false, }; static const struct of_device_id pm8941_pwr_key_id_table[] = { diff --git a/drivers/input/misc/tps6594-pwrbutton.c b/drivers/input/misc/tps6594-pwrbutton.c new file mode 100644 index 000000000000..cd039b3866dc --- /dev/null +++ b/drivers/input/misc/tps6594-pwrbutton.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * power button driver for TI TPS6594 PMICs + * + * Copyright (C) 2025 Critical Link LLC - https://www.criticallink.com/ + */ +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/tps6594.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +struct tps6594_pwrbutton { + struct device *dev; + struct input_dev *idev; + char phys[32]; +}; + +static irqreturn_t tps6594_pb_push_irq(int irq, void *_pwr) +{ + struct tps6594_pwrbutton *pwr = _pwr; + + input_report_key(pwr->idev, KEY_POWER, 1); + pm_wakeup_event(pwr->dev, 0); + input_sync(pwr->idev); + + return IRQ_HANDLED; +} + +static irqreturn_t tps6594_pb_release_irq(int irq, void *_pwr) +{ + struct tps6594_pwrbutton *pwr = _pwr; + + input_report_key(pwr->idev, KEY_POWER, 0); + input_sync(pwr->idev); + + return IRQ_HANDLED; +} + +static int tps6594_pb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tps6594_pwrbutton *pwr; + struct input_dev *idev; + int error; + int push_irq; + int release_irq; + + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); + if (!pwr) + return -ENOMEM; + + idev = devm_input_allocate_device(dev); + if (!idev) + return -ENOMEM; + + idev->name = pdev->name; + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", + pdev->name); + idev->phys = pwr->phys; + idev->id.bustype = BUS_I2C; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + pwr->dev = dev; + pwr->idev = idev; + device_init_wakeup(dev, true); + + push_irq = platform_get_irq(pdev, 0); + if (push_irq < 0) + return -EINVAL; + + release_irq = platform_get_irq(pdev, 1); + if (release_irq < 0) + return -EINVAL; + + error = devm_request_threaded_irq(dev, push_irq, NULL, + tps6594_pb_push_irq, + IRQF_ONESHOT, + pdev->resource[0].name, pwr); + if (error) { + dev_err(dev, "failed to request push IRQ #%d: %d\n", push_irq, + error); + return error; + } + + error = devm_request_threaded_irq(dev, release_irq, NULL, + tps6594_pb_release_irq, + IRQF_ONESHOT, + pdev->resource[1].name, pwr); + if (error) { + dev_err(dev, "failed to request release IRQ #%d: %d\n", + release_irq, error); + return error; + } + + error = input_register_device(idev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + return 0; +} + +static const struct platform_device_id tps6594_pwrbtn_id_table[] = { + { "tps6594-pwrbutton", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps6594_pwrbtn_id_table); + +static struct platform_driver tps6594_pb_driver = { + .probe = tps6594_pb_probe, + .driver = { + .name = "tps6594_pwrbutton", + }, + .id_table = tps6594_pwrbtn_id_table, +}; +module_platform_driver(tps6594_pb_driver); + +MODULE_DESCRIPTION("TPS6594 Power Button"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 2c51ea9d01d7..13336a2fd49c 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -775,6 +775,7 @@ static int uinput_ff_upload_to_user(char __user *buffer, if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; + memset(&ff_up_compat, 0, sizeof(ff_up_compat)); ff_up_compat.request_id = ff_up->request_id; ff_up_compat.retval = ff_up->retval; /* |