summaryrefslogtreecommitdiff
path: root/drivers/input/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/88pm80x_onkey.c20
-rw-r--r--drivers/input/misc/88pm860x_onkey.c13
-rw-r--r--drivers/input/misc/88pm886-onkey.c98
-rw-r--r--drivers/input/misc/Kconfig294
-rw-r--r--drivers/input/misc/Makefile25
-rw-r--r--drivers/input/misc/ab8500-ponkey.c12
-rw-r--r--drivers/input/misc/ad714x-i2c.c32
-rw-r--r--drivers/input/misc/ad714x-spi.c17
-rw-r--r--drivers/input/misc/ad714x.c28
-rw-r--r--drivers/input/misc/ad714x.h7
-rw-r--r--drivers/input/misc/adxl34x-i2c.c41
-rw-r--r--drivers/input/misc/adxl34x-spi.c36
-rw-r--r--drivers/input/misc/adxl34x.c179
-rw-r--r--drivers/input/misc/adxl34x.h8
-rw-r--r--drivers/input/misc/apanel.c183
-rw-r--r--drivers/input/misc/ariel-pwrbutton.c170
-rw-r--r--drivers/input/misc/arizona-haptics.c19
-rw-r--r--drivers/input/misc/atc260x-onkey.c305
-rw-r--r--drivers/input/misc/ati_remote2.c118
-rw-r--r--drivers/input/misc/atlas_btns.c21
-rw-r--r--drivers/input/misc/atmel_captouch.c19
-rw-r--r--drivers/input/misc/aw86927.c846
-rw-r--r--drivers/input/misc/axp20x-pek.c111
-rw-r--r--drivers/input/misc/bma150.c215
-rw-r--r--drivers/input/misc/cm109.c181
-rw-r--r--drivers/input/misc/cma3000_d0x.c34
-rw-r--r--drivers/input/misc/cma3000_d0x.h13
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c30
-rw-r--r--drivers/input/misc/cobalt_btns.c88
-rw-r--r--drivers/input/misc/cpcap-pwrbutton.c19
-rw-r--r--drivers/input/misc/cs40l50-vibra.c558
-rw-r--r--drivers/input/misc/da7280.c1328
-rw-r--r--drivers/input/misc/da9052_onkey.c12
-rw-r--r--drivers/input/misc/da9055_onkey.c17
-rw-r--r--drivers/input/misc/da9063_onkey.c121
-rw-r--r--drivers/input/misc/dm355evm_keys.c242
-rw-r--r--drivers/input/misc/drv260x.c159
-rw-r--r--drivers/input/misc/drv2665.c83
-rw-r--r--drivers/input/misc/drv2667.c87
-rw-r--r--drivers/input/misc/e3x0-button.c30
-rw-r--r--drivers/input/misc/gp2ap002a00f.c284
-rw-r--r--drivers/input/misc/gpio-beeper.c8
-rw-r--r--drivers/input/misc/gpio-vibra.c199
-rw-r--r--drivers/input/misc/gpio_decoder.c52
-rw-r--r--drivers/input/misc/hisi_powerkey.c10
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c344
-rw-r--r--drivers/input/misc/ibm-panel.c196
-rw-r--r--drivers/input/misc/ideapad_slidebar.c38
-rw-r--r--drivers/input/misc/ims-pcu.c262
-rw-r--r--drivers/input/misc/iqs269a.c1976
-rw-r--r--drivers/input/misc/iqs626a.c1821
-rw-r--r--drivers/input/misc/iqs7222.c3162
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c177
-rw-r--r--drivers/input/misc/keyspan_remote.c16
-rw-r--r--drivers/input/misc/kxtj9.c291
-rw-r--r--drivers/input/misc/m68kspkr.c11
-rw-r--r--drivers/input/misc/max7360-rotary.c192
-rw-r--r--drivers/input/misc/max77650-onkey.c129
-rw-r--r--drivers/input/misc/max77693-haptic.c89
-rw-r--r--drivers/input/misc/max8925_onkey.c19
-rw-r--r--drivers/input/misc/max8997_haptic.c146
-rw-r--r--drivers/input/misc/mc13783-pwrbutton.c7
-rw-r--r--drivers/input/misc/mma8450.c143
-rw-r--r--drivers/input/misc/nxp-bbnsm-pwrkey.c239
-rw-r--r--drivers/input/misc/palmas-pwrbutton.c38
-rw-r--r--drivers/input/misc/pcap_keys.c12
-rw-r--r--drivers/input/misc/pcf50633-input.c120
-rw-r--r--drivers/input/misc/pcf8574_keypad.c27
-rw-r--r--drivers/input/misc/pcspkr.c12
-rw-r--r--drivers/input/misc/pf1550-onkey.c197
-rw-r--r--drivers/input/misc/pm8941-pwrkey.c286
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c109
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c19
-rw-r--r--drivers/input/misc/powermate.c17
-rw-r--r--drivers/input/misc/pwm-beeper.c56
-rw-r--r--drivers/input/misc/pwm-vibra.c93
-rw-r--r--drivers/input/misc/qnap-mcu-input.c153
-rw-r--r--drivers/input/misc/rb532_button.c49
-rw-r--r--drivers/input/misc/regulator-haptic.c40
-rw-r--r--drivers/input/misc/rk805-pwrkey.c15
-rw-r--r--drivers/input/misc/rotary_encoder.c47
-rw-r--r--drivers/input/misc/rt5120-pwrkey.c120
-rw-r--r--drivers/input/misc/sc27xx-vibra.c71
-rw-r--r--drivers/input/misc/sgi_btns.c70
-rw-r--r--drivers/input/misc/sirfsoc-onkey.c208
-rw-r--r--drivers/input/misc/soc_button_array.c322
-rw-r--r--drivers/input/misc/sparcspkr.c88
-rw-r--r--drivers/input/misc/stpmic1_onkey.c192
-rw-r--r--drivers/input/misc/tps65218-pwrbutton.c14
-rw-r--r--drivers/input/misc/tps65219-pwrbutton.c150
-rw-r--r--drivers/input/misc/tps6594-pwrbutton.c126
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c5
-rw-r--r--drivers/input/misc/twl4030-vibra.c50
-rw-r--r--drivers/input/misc/twl6040-vibra.c35
-rw-r--r--drivers/input/misc/uinput.c115
-rw-r--r--drivers/input/misc/wistron_btns.c83
-rw-r--r--drivers/input/misc/wm831x-on.c6
-rw-r--r--drivers/input/misc/xen-kbdfront.c16
-rw-r--r--drivers/input/misc/yealink.c106
-rw-r--r--drivers/input/misc/yealink.h16
100 files changed, 14537 insertions, 4176 deletions
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c
index 45a09497f680..9159b5fec129 100644
--- a/drivers/input/misc/88pm80x_onkey.c
+++ b/drivers/input/misc/88pm80x_onkey.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell 88PM80x ONKEY driver
*
* Copyright (C) 2012 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
* Qiao Zhou <zhouqiao@marvell.com>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file "COPYING" in the main directory of this
- * archive for more details.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -69,7 +57,7 @@ static int pm80x_onkey_probe(struct platform_device *pdev)
struct pm80x_onkey_info *info;
int err;
- info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -77,7 +65,6 @@ static int pm80x_onkey_probe(struct platform_device *pdev)
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
err = -EINVAL;
goto out;
}
@@ -139,14 +126,13 @@ out:
return err;
}
-static int pm80x_onkey_remove(struct platform_device *pdev)
+static void pm80x_onkey_remove(struct platform_device *pdev)
{
struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
pm80x_free_irq(info->pm80x, info->irq, info);
input_unregister_device(info->idev);
kfree(info);
- return 0;
}
static struct platform_driver pm80x_onkey_driver = {
diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c
index cc87443aa2ee..0f8b7ffb3aff 100644
--- a/drivers/input/misc/88pm860x_onkey.c
+++ b/drivers/input/misc/88pm860x_onkey.c
@@ -64,10 +64,8 @@ static int pm860x_onkey_probe(struct platform_device *pdev)
int irq, ret;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
+ if (irq < 0)
return -EINVAL;
- }
info = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_onkey_info),
GFP_KERNEL);
@@ -112,7 +110,7 @@ static int pm860x_onkey_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused pm860x_onkey_suspend(struct device *dev)
+static int pm860x_onkey_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
@@ -121,7 +119,7 @@ static int __maybe_unused pm860x_onkey_suspend(struct device *dev)
chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY;
return 0;
}
-static int __maybe_unused pm860x_onkey_resume(struct device *dev)
+static int pm860x_onkey_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
@@ -131,12 +129,13 @@ static int __maybe_unused pm860x_onkey_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops,
+ pm860x_onkey_suspend, pm860x_onkey_resume);
static struct platform_driver pm860x_onkey_driver = {
.driver = {
.name = "88pm860x-onkey",
- .pm = &pm860x_onkey_pm_ops,
+ .pm = pm_sleep_ptr(&pm860x_onkey_pm_ops),
},
.probe = pm860x_onkey_probe,
};
diff --git a/drivers/input/misc/88pm886-onkey.c b/drivers/input/misc/88pm886-onkey.c
new file mode 100644
index 000000000000..284ff5190b6e
--- /dev/null
+++ b/drivers/input/misc/88pm886-onkey.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/88pm886.h>
+
+struct pm886_onkey {
+ struct input_dev *idev;
+ struct pm886_chip *chip;
+};
+
+static irqreturn_t pm886_onkey_irq_handler(int irq, void *data)
+{
+ struct pm886_onkey *onkey = data;
+ struct regmap *regmap = onkey->chip->regmap;
+ struct input_dev *idev = onkey->idev;
+ struct device *parent = idev->dev.parent;
+ unsigned int val;
+ int err;
+
+ err = regmap_read(regmap, PM886_REG_STATUS1, &val);
+ if (err) {
+ dev_err(parent, "Failed to read status: %d\n", err);
+ return IRQ_NONE;
+ }
+ val &= PM886_ONKEY_STS1;
+
+ input_report_key(idev, KEY_POWER, val);
+ input_sync(idev);
+
+ return IRQ_HANDLED;
+}
+
+static int pm886_onkey_probe(struct platform_device *pdev)
+{
+ struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct pm886_onkey *onkey;
+ struct input_dev *idev;
+ int irq, err;
+
+ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ onkey->chip = chip;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");
+
+ idev = devm_input_allocate_device(dev);
+ if (!idev) {
+ dev_err(dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ onkey->idev = idev;
+
+ idev->name = "88pm886-onkey";
+ idev->phys = "88pm886-onkey/input0";
+ idev->id.bustype = BUS_I2C;
+
+ input_set_capability(idev, EV_KEY, KEY_POWER);
+
+ err = devm_request_threaded_irq(dev, irq, NULL, pm886_onkey_irq_handler,
+ IRQF_ONESHOT | IRQF_NO_SUSPEND, "onkey",
+ onkey);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to request IRQ\n");
+
+ err = input_register_device(idev);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register input device\n");
+
+ return 0;
+}
+
+static const struct platform_device_id pm886_onkey_id_table[] = {
+ { "88pm886-onkey", },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, pm886_onkey_id_table);
+
+static struct platform_driver pm886_onkey_driver = {
+ .driver = {
+ .name = "88pm886-onkey",
+ },
+ .probe = pm886_onkey_probe,
+ .id_table = pm886_onkey_id_table,
+};
+module_platform_driver(pm886_onkey_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM886 onkey driver");
+MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2be9bc5..94a753fcb64f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Input misc drivers configuration
#
@@ -32,6 +33,13 @@ config INPUT_88PM80X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm80x_onkey.
+config INPUT_88PM886_ONKEY
+ tristate "Marvell 88PM886 onkey support"
+ depends on MFD_88PM886_PMIC
+ help
+ Support the onkey of Marvell 88PM886 PMIC as an input device
+ reporting power button status.
+
config INPUT_AB8500_PONKEY
tristate "AB8500 Pon (PowerOn) Key"
depends on AB8500_CORE
@@ -72,6 +80,17 @@ config INPUT_AD714X_SPI
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
+config INPUT_ARIEL_PWRBUTTON
+ tristate "Dell Wyse 3020 Power Button Driver"
+ depends on SPI
+ depends on MACH_MMP3_DT || COMPILE_TEST
+ help
+ Say Y to enable support for reporting power button status on
+ on Dell Wyse 3020 ("Ariel") thin client.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ariel-pwrbutton.
+
config INPUT_ARIZONA_HAPTICS
tristate "Arizona haptics support"
depends on MFD_ARIZONA && SND_SOC
@@ -82,9 +101,20 @@ config INPUT_ARIZONA_HAPTICS
To compile this driver as a module, choose M here: the
module will be called arizona-haptics.
+config INPUT_ATC260X_ONKEY
+ tristate "Actions Semi ATC260x PMIC ONKEY"
+ depends on MFD_ATC260X
+ help
+ Support the ONKEY of ATC260x PMICs as an input device reporting
+ power button status. ONKEY can be used to wakeup from low power
+ modes and force a reset on long press.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atc260x-onkey.
+
config INPUT_ATMEL_CAPTOUCH
tristate "Atmel Capacitive Touch Button Driver"
- depends on OF || COMPILE_TEST
+ depends on OF
depends on I2C
help
Say Y here if an Atmel Capacitive Touch Button device which
@@ -96,10 +126,31 @@ 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
+ depends on OF
+ help
+ This is the bbnsm powerkey driver for the NXP i.MX application
+ processors.
+
+ To compile this driver as a module, choose M here; the
+ module will be called bbnsm_pwrkey.
+
config INPUT_BMA150
tristate "BMA150/SMB380 acceleration sensor support"
depends on I2C
- select INPUT_POLLDEV
help
Say Y here if you have Bosch Sensortec's BMA150 or SMB380
acceleration sensor hooked to an I2C bus.
@@ -107,6 +158,16 @@ config INPUT_BMA150
To compile this driver as a module, choose M here: the
module will be called bma150.
+config INPUT_CS40L50_VIBRA
+ tristate "CS40L50 Haptic Driver support"
+ depends on MFD_CS40L50_CORE
+ help
+ Say Y here to enable support for Cirrus Logic's CS40L50
+ haptic driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs40l50-vibra.
+
config INPUT_E3X0_BUTTON
tristate "NI Ettus Research USRP E3xx Button support."
default n
@@ -129,6 +190,17 @@ config INPUT_PCSPKR
To compile this driver as a module, choose M here: the
module will be called pcspkr.
+config INPUT_PF1550_ONKEY
+ tristate "NXP PF1550 Onkey support"
+ depends on MFD_PF1550
+ help
+ Say Y here if you want support for PF1550 PMIC. Onkey can trigger
+ release and 1s(push hold), 2s, 3s, 4s, 8s interrupt for long press
+ detect.
+
+ To compile this driver as a module, choose M here. The module will be
+ called pf1550-onkey.
+
config INPUT_PM8941_PWRKEY
tristate "Qualcomm PM8941 power key support"
depends on MFD_SPMI_PMIC
@@ -180,13 +252,32 @@ 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
+ help
+ Support the ONKEY of the MAX77650 PMIC as an input device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called max77650-onkey.
+
config INPUT_MAX77693_HAPTIC
- tristate "MAXIM MAX77693/MAX77843 haptic controller support"
- depends on (MFD_MAX77693 || MFD_MAX77843) && PWM
+ tristate "MAXIM MAX77693/MAX77705/MAX77843 haptic controller support"
+ depends on (MFD_MAX77693 || MFD_MAX77705 || MFD_MAX77843) && PWM
select INPUT_FF_MEMLESS
help
This option enables support for the haptic controller on
- MAXIM MAX77693 and MAX77843 chips.
+ MAXIM MAX77693, MAX77705 and MAX77843 chips.
To compile this driver as module, choose M here: the
module will be called max77693-haptic.
@@ -226,7 +317,6 @@ config INPUT_MC13783_PWRBUTTON
config INPUT_MMA8450
tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
depends on I2C
- select INPUT_POLLDEV
help
Say Y here if you want to support Freescale's MMA8450 Accelerometer
through I2C interface.
@@ -237,7 +327,6 @@ config INPUT_MMA8450
config INPUT_APANEL
tristate "Fujitsu Lifebook Application Panel buttons"
depends on X86 && I2C && LEDS_CLASS
- select INPUT_POLLDEV
select CHECK_SIGNATURE
help
Say Y here for support of the Application Panel buttons, used on
@@ -248,17 +337,6 @@ config INPUT_APANEL
To compile this driver as a module, choose M here: the module will
be called apanel.
-config INPUT_GP2A
- tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
- depends on I2C
- depends on GPIOLIB || COMPILE_TEST
- help
- Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip
- hooked to an I2C bus.
-
- To compile this driver as a module, choose M here: the
- module will be called gp2ap002a00f.
-
config INPUT_GPIO_BEEPER
tristate "Generic GPIO Beeper support"
depends on GPIOLIB || COMPILE_TEST
@@ -271,7 +349,6 @@ config INPUT_GPIO_BEEPER
config INPUT_GPIO_DECODER
tristate "Polled GPIO Decoder Input driver"
depends on GPIOLIB || COMPILE_TEST
- select INPUT_POLLDEV
help
Say Y here if you want driver to read status of multiple GPIO
lines and report the encoded value as an absolute integer to
@@ -280,22 +357,21 @@ config INPUT_GPIO_DECODER
To compile this driver as a module, choose M here: the module
will be called gpio_decoder.
-config INPUT_IXP4XX_BEEPER
- tristate "IXP4XX Beeper support"
- depends on ARCH_IXP4XX
+config INPUT_GPIO_VIBRA
+ tristate "GPIO vibrator support"
+ depends on GPIOLIB || COMPILE_TEST
+ select INPUT_FF_MEMLESS
help
- If you say yes here, you can connect a beeper to the
- ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+ Say Y here to get support for GPIO based vibrator devices.
- If unsure, say Y.
+ If unsure, say N.
- To compile this driver as a module, choose M here: the
- module will be called ixp4xx-beeper.
+ To compile this driver as a module, choose M here: the module will be
+ called gpio-vibra.
config INPUT_COBALT_BTNS
tristate "Cobalt button interface"
depends on MIPS_COBALT
- select INPUT_POLLDEV
help
Say Y here if you want to support MIPS Cobalt button interface.
@@ -314,8 +390,7 @@ config INPUT_CPCAP_PWRBUTTON
config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
- depends on X86_32
- select INPUT_POLLDEV
+ depends on X86_32 && !UML
select INPUT_SPARSEKMAP
select NEW_LEDS
select LEDS_CLASS
@@ -378,13 +453,6 @@ config INPUT_KXTJ9
To compile this driver as a module, choose M here: the module will
be called kxtj9.
-config INPUT_KXTJ9_POLLED_MODE
- bool "Enable polling mode support"
- depends on INPUT_KXTJ9
- select INPUT_POLLDEV
- help
- Say Y here if you need accelerometer to work in polling mode.
-
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
@@ -460,6 +528,26 @@ config INPUT_TPS65218_PWRBUTTON
To compile this driver as a module, choose M here. The module will
be called tps65218-pwrbutton.
+config INPUT_TPS65219_PWRBUTTON
+ tristate "TPS65219 Power button driver"
+ depends on MFD_TPS65219
+ help
+ Say Y here if you want to enable power button reporting for
+ TPS65219 Power Management IC devices.
+
+ 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
@@ -514,7 +602,6 @@ config INPUT_UINPUT
config INPUT_SGI_BTNS
tristate "SGI Indy/O2 volume button interface"
depends on SGI_IP22 || SGI_IP32
- select INPUT_POLLDEV
help
Say Y here if you want to support SGI Indy/O2 volume button interface.
@@ -539,13 +626,6 @@ config INPUT_PALMAS_PWRBUTTON
To compile this driver as a module, choose M here. The module will
be called palmas_pwrbutton.
-config INPUT_PCF50633_PMU
- tristate "PCF50633 PMU events"
- depends on MFD_PCF50633
- help
- Say Y to include support for delivering PMU events via input
- layer on NXP PCF50633.
-
config INPUT_PCF8574
tristate "PCF8574 Keypad input device"
depends on I2C
@@ -581,7 +661,7 @@ config INPUT_PWM_VIBRA
config INPUT_RK805_PWRKEY
tristate "Rockchip RK805 PMIC power key support"
- depends on MFD_RK808
+ depends on MFD_RK8XX
help
Select this option to enable power key driver for RK805.
@@ -605,7 +685,6 @@ config INPUT_RB532_BUTTON
tristate "Mikrotik Routerboard 532 button interface"
depends on MIKROTIK_RB532
depends on GPIOLIB
- select INPUT_POLLDEV
help
Say Y here if you want support for the S1 button built into
Mikrotik's Routerboard 532.
@@ -613,6 +692,18 @@ config INPUT_RB532_BUTTON
To compile this driver as a module, choose M here: the
module will be called rb532_button.
+config INPUT_DA7280_HAPTICS
+ tristate "Dialog Semiconductor DA7280 haptics support"
+ depends on INPUT && I2C
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the Dialog DA7280 haptics driver.
+ The haptics can be controlled by PWM or GPIO
+ with I2C communication.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da7280.
+
config INPUT_DA9052_ONKEY
tristate "Dialog DA9052/DA9053 Onkey"
depends on PMIC_DA9052
@@ -644,17 +735,6 @@ config INPUT_DA9063_ONKEY
To compile this driver as a module, choose M here: the module
will be called da9063_onkey.
-config INPUT_DM355EVM
- tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
- depends on MFD_DM355EVM_MSP
- select INPUT_SPARSEKMAP
- help
- Supports the pushbuttons and IR remote used with
- the DM355 EVM board.
-
- To compile this driver as a module, choose M here: the
- module will be called dm355evm_keys.
-
config INPUT_WM831X_ON
tristate "WM831X ON pin"
depends on MFD_WM831X
@@ -712,6 +792,24 @@ config INPUT_ADXL34X_SPI
To compile this driver as a module, choose M here: the
module will be called adxl34x-spi.
+config INPUT_IBM_PANEL
+ tristate "IBM Operation Panel driver"
+ depends on I2C && I2C_SLAVE
+ help
+ Say Y here if you have an IBM Operation Panel connected to your system
+ over I2C. The panel is typically connected only to a system's service
+ processor (BMC).
+
+ If unsure, say N.
+
+ The Operation Panel is a controller with some buttons and an LCD
+ display that allows someone with physical access to the system to
+ perform various administrative tasks. This driver only supports the part
+ of the controller that sends commands to the system.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ibm-panel.
+
config INPUT_IMS_PCU
tristate "IMS Passenger Control Unit driver"
depends on USB
@@ -722,6 +820,38 @@ config INPUT_IMS_PCU
To compile this driver as a module, choose M here: the module will be
called ims_pcu.
+config INPUT_IQS269A
+ tristate "Azoteq IQS269A capacitive touch controller"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the Azoteq IQS269A capacitive
+ touch controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iqs269a.
+
+config INPUT_IQS626A
+ tristate "Azoteq IQS626A capacitive touch controller"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the Azoteq IQS626A capacitive
+ touch controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iqs626a.
+
+config INPUT_IQS7222
+ tristate "Azoteq IQS7222A/B/C/D capacitive touch controller"
+ depends on I2C
+ help
+ Say Y to enable support for the Azoteq IQS7222A/B/C/D family
+ of capacitive touch controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iqs7222.
+
config INPUT_CMA3000
tristate "VTI CMA3000 Tri-axis accelerometer"
help
@@ -759,16 +889,6 @@ config INPUT_XEN_KBDDEV_FRONTEND
To compile this driver as a module, choose M here: the
module will be called xen-kbdfront.
-config INPUT_SIRFSOC_ONKEY
- tristate "CSR SiRFSoC power on/off/suspend key support"
- depends on ARCH_SIRF && OF
- default y
- help
- Say Y here if you want to support for the SiRFSoC power on/off/suspend key
- in Linux, after you press the onkey, system will suspend.
-
- If unsure, say N.
-
config INPUT_IDEAPAD_SLIDEBAR
tristate "IdeaPad Laptop Slidebar"
depends on INPUT
@@ -781,10 +901,10 @@ config INPUT_IDEAPAD_SLIDEBAR
config INPUT_SOC_BUTTON_ARRAY
tristate "Windows-compatible SoC Button Array"
- depends on KEYBOARD_GPIO
+ depends on KEYBOARD_GPIO && ACPI
help
- Say Y here if you have a SoC-based tablet that originally
- runs Windows 8.
+ Say Y here if you have a SoC-based tablet that originally runs
+ Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later.
To compile this driver as a module, choose M here: the
module will be called soc_button_array.
@@ -832,6 +952,18 @@ config INPUT_HISI_POWERKEY
To compile this driver as a module, choose M here: the
module will be called hisi_powerkey.
+config INPUT_QNAP_MCU
+ tristate "Input Support for QNAP MCU controllers"
+ depends on MFD_QNAP_MCU
+ help
+ This option enables support for input elements available on
+ embedded controllers used in QNAP NAS devices.
+
+ This includes a polled power-button as well as a beeper.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qnap-mcu-input.
+
config INPUT_RAVE_SP_PWRBUTTON
tristate "RAVE SP Power button Driver"
depends on RAVE_SP_CORE
@@ -851,4 +983,24 @@ config INPUT_SC27XX_VIBRA
To compile this driver as a module, choose M here. The module will
be called sc27xx_vibra.
+config INPUT_RT5120_PWRKEY
+ tristate "RT5120 PMIC power key support"
+ depends on MFD_RT5120 || COMPILE_TEST
+ help
+ This enables support for RT5120 PMIC power key driver.
+
+ To compile this driver as a module, choose M here. the module will
+ be called rt5120-pwrkey.
+
+config INPUT_STPMIC1_ONKEY
+ tristate "STPMIC1 PMIC Onkey support"
+ depends on MFD_STPMIC1
+ help
+ Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey
+ can be used to wakeup from low power modes and force a shut-down on
+ long press.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1_onkey.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1ff68f..415fc4e2918b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o
+obj-$(CONFIG_INPUT_88PM886_ONKEY) += 88pm886-onkey.o
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
@@ -15,34 +16,44 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_ARIEL_PWRBUTTON) += ariel-pwrbutton.o
obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
+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
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o
+obj-$(CONFIG_INPUT_CS40L50_VIBRA) += cs40l50-vibra.o
+obj-$(CONFIG_INPUT_DA7280_HAPTICS) += da7280.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o
-obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
-obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
+obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
+obj-$(CONFIG_INPUT_IBM_PANEL) += ibm-panel.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
-obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
+obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o
+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
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
@@ -50,28 +61,32 @@ obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
-obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_PF1550_ONKEY) += pf1550-onkey.o
obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o
obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o
+obj-$(CONFIG_INPUT_QNAP_MCU) += qnap-mcu-input.o
obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
+obj-$(CONFIG_INPUT_RT5120_PWRKEY) += rt5120-pwrkey.o
obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o
obj-$(CONFIG_INPUT_SC27XX_VIBRA) += sc27xx-vibra.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
-obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
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/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
index a33ed5710b15..a9b9013680d6 100644
--- a/drivers/input/misc/ab8500-ponkey.c
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) ST-Ericsson SA 2010
*
- * License Terms: GNU General Public License v2
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
*
* AB8500 Power-On Key handler
@@ -19,7 +19,7 @@
/**
* struct ab8500_ponkey - ab8500 ponkey information
- * @input_dev: pointer to input device
+ * @idev: pointer to input device
* @ab8500: ab8500 parent
* @irq_dbf: irq number for falling transition
* @irq_dbr: irq number for rising transition
@@ -55,16 +55,12 @@ static int ab8500_ponkey_probe(struct platform_device *pdev)
int error;
irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
- if (irq_dbf < 0) {
- dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
+ if (irq_dbf < 0)
return irq_dbf;
- }
irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
- if (irq_dbr < 0) {
- dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
+ if (irq_dbr < 0)
return irq_dbr;
- }
ponkey = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_ponkey),
GFP_KERNEL);
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index 2f047738bc0b..2adb7a058362 100644
--- a/drivers/input/misc/ad714x-i2c.c
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD714X CapTouch Programmable Controller driver (I2C bus)
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_I2C */
@@ -13,18 +12,6 @@
#include <linux/pm.h>
#include "ad714x.h"
-static int __maybe_unused ad714x_i2c_suspend(struct device *dev)
-{
- return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev)));
-}
-
-static int __maybe_unused ad714x_i2c_resume(struct device *dev)
-{
- return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev)));
-}
-
-static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume);
-
static int ad714x_i2c_write(struct ad714x_chip *chip,
unsigned short reg, unsigned short data)
{
@@ -70,8 +57,7 @@ static int ad714x_i2c_read(struct ad714x_chip *chip,
return 0;
}
-static int ad714x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ad714x_i2c_probe(struct i2c_client *client)
{
struct ad714x_chip *chip;
@@ -86,11 +72,11 @@ static int ad714x_i2c_probe(struct i2c_client *client,
}
static const struct i2c_device_id ad714x_id[] = {
- { "ad7142_captouch", 0 },
- { "ad7143_captouch", 0 },
- { "ad7147_captouch", 0 },
- { "ad7147a_captouch", 0 },
- { "ad7148_captouch", 0 },
+ { "ad7142_captouch" },
+ { "ad7143_captouch" },
+ { "ad7147_captouch" },
+ { "ad7147a_captouch" },
+ { "ad7148_captouch" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad714x_id);
@@ -98,9 +84,9 @@ MODULE_DEVICE_TABLE(i2c, ad714x_id);
static struct i2c_driver ad714x_i2c_driver = {
.driver = {
.name = "ad714x_captouch",
- .pm = &ad714x_i2c_pm,
+ .pm = pm_sleep_ptr(&ad714x_pm),
},
- .probe = ad714x_i2c_probe,
+ .probe = ad714x_i2c_probe,
.id_table = ad714x_id,
};
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index aac910326447..eb13b4cd6594 100644
--- a/drivers/input/misc/ad714x-spi.c
+++ b/drivers/input/misc/ad714x-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD714X CapTouch Programmable Controller driver (SPI bus)
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_SPI */
@@ -16,18 +15,6 @@
#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */
#define AD714x_SPI_READ BIT(10)
-static int __maybe_unused ad714x_spi_suspend(struct device *dev)
-{
- return ad714x_disable(spi_get_drvdata(to_spi_device(dev)));
-}
-
-static int __maybe_unused ad714x_spi_resume(struct device *dev)
-{
- return ad714x_enable(spi_get_drvdata(to_spi_device(dev)));
-}
-
-static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume);
-
static int ad714x_spi_read(struct ad714x_chip *chip,
unsigned short reg, unsigned short *data, size_t len)
{
@@ -104,7 +91,7 @@ static int ad714x_spi_probe(struct spi_device *spi)
static struct spi_driver ad714x_spi_driver = {
.driver = {
.name = "ad714x_captouch",
- .pm = &ad714x_spi_pm,
+ .pm = pm_sleep_ptr(&ad714x_pm),
},
.probe = ad714x_spi_probe,
};
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
index 84b51dd51f6e..c9fa789337ba 100644
--- a/drivers/input/misc/ad714x.c
+++ b/drivers/input/misc/ad714x.c
@@ -1,12 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
+#include <linux/export.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -942,7 +942,7 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
struct ad714x_chip *ad714x = data;
int i;
- mutex_lock(&ad714x->mutex);
+ guard(mutex)(&ad714x->mutex);
ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
@@ -955,8 +955,6 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
for (i = 0; i < ad714x->hw->touchpad_num; i++)
ad714x_touchpad_state_machine(ad714x, i);
- mutex_unlock(&ad714x->mutex);
-
return IRQ_HANDLED;
}
@@ -1163,29 +1161,27 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
}
EXPORT_SYMBOL(ad714x_probe);
-#ifdef CONFIG_PM
-int ad714x_disable(struct ad714x_chip *ad714x)
+static int ad714x_suspend(struct device *dev)
{
+ struct ad714x_chip *ad714x = dev_get_drvdata(dev);
unsigned short data;
dev_dbg(ad714x->dev, "%s enter\n", __func__);
- mutex_lock(&ad714x->mutex);
+ guard(mutex)(&ad714x->mutex);
data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
ad714x->write(ad714x, AD714X_PWR_CTRL, data);
- mutex_unlock(&ad714x->mutex);
-
return 0;
}
-EXPORT_SYMBOL(ad714x_disable);
-int ad714x_enable(struct ad714x_chip *ad714x)
+static int ad714x_resume(struct device *dev)
{
+ struct ad714x_chip *ad714x = dev_get_drvdata(dev);
dev_dbg(ad714x->dev, "%s enter\n", __func__);
- mutex_lock(&ad714x->mutex);
+ guard(mutex)(&ad714x->mutex);
/* resume to non-shutdown mode */
@@ -1198,12 +1194,10 @@ int ad714x_enable(struct ad714x_chip *ad714x)
ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
- mutex_unlock(&ad714x->mutex);
-
return 0;
}
-EXPORT_SYMBOL(ad714x_enable);
-#endif
+
+EXPORT_SIMPLE_DEV_PM_OPS(ad714x_pm, ad714x_suspend, ad714x_resume);
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h
index 5d65d303b9bf..dafa12325f27 100644
--- a/drivers/input/misc/ad714x.h
+++ b/drivers/input/misc/ad714x.h
@@ -1,14 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AD714X CapTouch Programmable Controller driver (bus interfaces)
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _AD714X_H_
#define _AD714X_H_
+#include <linux/pm.h>
#include <linux/types.h>
#define STAGE_NUM 12
@@ -46,8 +46,7 @@ struct ad714x_chip {
};
-int ad714x_disable(struct ad714x_chip *ad714x);
-int ad714x_enable(struct ad714x_chip *ad714x);
+extern const struct dev_pm_ops ad714x_pm;
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
ad714x_read_t read, ad714x_write_t write);
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index 7fed92fb8cc1..c05d898898e8 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface)
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
- * Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_I2C */
@@ -74,8 +74,7 @@ static const struct adxl34x_bus_ops adxl34x_i2c_bops = {
.read_block = adxl34x_i2c_read_block,
};
-static int adxl34x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adxl34x_i2c_probe(struct i2c_client *client)
{
struct adxl34x *ac;
int error;
@@ -99,38 +98,8 @@ static int adxl34x_i2c_probe(struct i2c_client *client,
return 0;
}
-static int adxl34x_i2c_remove(struct i2c_client *client)
-{
- struct adxl34x *ac = i2c_get_clientdata(client);
-
- return adxl34x_remove(ac);
-}
-
-static int __maybe_unused adxl34x_i2c_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adxl34x *ac = i2c_get_clientdata(client);
-
- adxl34x_suspend(ac);
-
- return 0;
-}
-
-static int __maybe_unused adxl34x_i2c_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adxl34x *ac = i2c_get_clientdata(client);
-
- adxl34x_resume(ac);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend,
- adxl34x_i2c_resume);
-
static const struct i2c_device_id adxl34x_id[] = {
- { "adxl34x", 0 },
+ { "adxl34x" },
{ }
};
@@ -156,11 +125,11 @@ MODULE_DEVICE_TABLE(of, adxl34x_of_id);
static struct i2c_driver adxl34x_driver = {
.driver = {
.name = "adxl34x",
- .pm = &adxl34x_i2c_pm,
+ .dev_groups = adxl34x_groups,
+ .pm = pm_sleep_ptr(&adxl34x_pm),
.of_match_table = adxl34x_of_id,
},
.probe = adxl34x_i2c_probe,
- .remove = adxl34x_i2c_remove,
.id_table = adxl34x_id,
};
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
index 3ec03ad88eed..fd716d861832 100644
--- a/drivers/input/misc/adxl34x-spi.c
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
- * Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_SPI */
@@ -87,43 +87,13 @@ static int adxl34x_spi_probe(struct spi_device *spi)
return 0;
}
-static int adxl34x_spi_remove(struct spi_device *spi)
-{
- struct adxl34x *ac = spi_get_drvdata(spi);
-
- return adxl34x_remove(ac);
-}
-
-static int __maybe_unused adxl34x_spi_suspend(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct adxl34x *ac = spi_get_drvdata(spi);
-
- adxl34x_suspend(ac);
-
- return 0;
-}
-
-static int __maybe_unused adxl34x_spi_resume(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct adxl34x *ac = spi_get_drvdata(spi);
-
- adxl34x_resume(ac);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
- adxl34x_spi_resume);
-
static struct spi_driver adxl34x_driver = {
.driver = {
.name = "adxl34x",
- .pm = &adxl34x_spi_pm,
+ .dev_groups = adxl34x_groups,
+ .pm = pm_sleep_ptr(&adxl34x_pm),
},
.probe = adxl34x_spi_probe,
- .remove = adxl34x_spi_remove,
};
module_spi_driver(adxl34x_driver);
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
index a3e79bf5a04b..ac7674647c09 100644
--- a/drivers/input/misc/adxl34x.c
+++ b/drivers/input/misc/adxl34x.c
@@ -1,14 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADXL345/346 Three-Axis Digital Accelerometers
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -237,11 +238,12 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
{
- short buf[3];
+ __le16 buf[3];
ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf);
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
+
ac->saved.x = (s16) le16_to_cpu(buf[0]);
axis->x = ac->saved.x;
@@ -250,7 +252,6 @@ static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
ac->saved.z = (s16) le16_to_cpu(buf[2]);
axis->z = ac->saved.z;
- mutex_unlock(&ac->mutex);
}
static void adxl34x_service_ev_fifo(struct adxl34x *ac)
@@ -412,31 +413,33 @@ static void __adxl34x_enable(struct adxl34x *ac)
AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
}
-void adxl34x_suspend(struct adxl34x *ac)
+static int adxl34x_suspend(struct device *dev)
{
- mutex_lock(&ac->mutex);
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ guard(mutex)(&ac->mutex);
if (!ac->suspended && !ac->disabled && ac->opened)
__adxl34x_disable(ac);
ac->suspended = true;
- mutex_unlock(&ac->mutex);
+ return 0;
}
-EXPORT_SYMBOL_GPL(adxl34x_suspend);
-void adxl34x_resume(struct adxl34x *ac)
+static int adxl34x_resume(struct device *dev)
{
- mutex_lock(&ac->mutex);
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ guard(mutex)(&ac->mutex);
if (ac->suspended && !ac->disabled && ac->opened)
__adxl34x_enable(ac);
ac->suspended = false;
- mutex_unlock(&ac->mutex);
+ return 0;
}
-EXPORT_SYMBOL_GPL(adxl34x_resume);
static ssize_t adxl34x_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -458,7 +461,7 @@ static ssize_t adxl34x_disable_store(struct device *dev,
if (error)
return error;
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
if (!ac->suspended && ac->opened) {
if (val) {
@@ -472,8 +475,6 @@ static ssize_t adxl34x_disable_store(struct device *dev,
ac->disabled = !!val;
- mutex_unlock(&ac->mutex);
-
return count;
}
@@ -483,16 +484,13 @@ static ssize_t adxl34x_calibrate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adxl34x *ac = dev_get_drvdata(dev);
- ssize_t count;
- mutex_lock(&ac->mutex);
- count = sprintf(buf, "%d,%d,%d\n",
- ac->hwcal.x * 4 + ac->swcal.x,
- ac->hwcal.y * 4 + ac->swcal.y,
- ac->hwcal.z * 4 + ac->swcal.z);
- mutex_unlock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
- return count;
+ return sprintf(buf, "%d,%d,%d\n",
+ ac->hwcal.x * 4 + ac->swcal.x,
+ ac->hwcal.y * 4 + ac->swcal.y,
+ ac->hwcal.z * 4 + ac->swcal.z);
}
static ssize_t adxl34x_calibrate_store(struct device *dev,
@@ -506,7 +504,8 @@ static ssize_t adxl34x_calibrate_store(struct device *dev,
* We use HW calibration and handle the remaining bits in SW. (4mg/LSB)
*/
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
+
ac->hwcal.x -= (ac->saved.x / 4);
ac->swcal.x = ac->saved.x % 4;
@@ -519,7 +518,6 @@ static ssize_t adxl34x_calibrate_store(struct device *dev,
AC_WRITE(ac, OFSX, (s8) ac->hwcal.x);
AC_WRITE(ac, OFSY, (s8) ac->hwcal.y);
AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z);
- mutex_unlock(&ac->mutex);
return count;
}
@@ -547,15 +545,13 @@ static ssize_t adxl34x_rate_store(struct device *dev,
if (error)
return error;
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
ac->pdata.data_rate = RATE(val);
AC_WRITE(ac, BW_RATE,
ac->pdata.data_rate |
(ac->pdata.low_power_mode ? LOW_POWER : 0));
- mutex_unlock(&ac->mutex);
-
return count;
}
@@ -582,7 +578,7 @@ static ssize_t adxl34x_autosleep_store(struct device *dev,
if (error)
return error;
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
if (val)
ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK);
@@ -592,8 +588,6 @@ static ssize_t adxl34x_autosleep_store(struct device *dev,
if (!ac->disabled && !ac->suspended && ac->opened)
AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
- mutex_unlock(&ac->mutex);
-
return count;
}
@@ -604,14 +598,11 @@ static ssize_t adxl34x_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adxl34x *ac = dev_get_drvdata(dev);
- ssize_t count;
- mutex_lock(&ac->mutex);
- count = sprintf(buf, "(%d, %d, %d)\n",
- ac->saved.x, ac->saved.y, ac->saved.z);
- mutex_unlock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
- return count;
+ return sprintf(buf, "(%d, %d, %d)\n",
+ ac->saved.x, ac->saved.y, ac->saved.z);
}
static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL);
@@ -632,9 +623,8 @@ static ssize_t adxl34x_write_store(struct device *dev,
if (error)
return error;
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
AC_WRITE(ac, val >> 8, val & 0xFF);
- mutex_unlock(&ac->mutex);
return count;
}
@@ -658,19 +648,23 @@ static const struct attribute_group adxl34x_attr_group = {
.attrs = adxl34x_attributes,
};
+const struct attribute_group *adxl34x_groups[] = {
+ &adxl34x_attr_group,
+ NULL
+};
+EXPORT_SYMBOL_GPL(adxl34x_groups);
+
static int adxl34x_input_open(struct input_dev *input)
{
struct adxl34x *ac = input_get_drvdata(input);
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
if (!ac->suspended && !ac->disabled)
__adxl34x_enable(ac);
ac->opened = true;
- mutex_unlock(&ac->mutex);
-
return 0;
}
@@ -678,14 +672,12 @@ static void adxl34x_input_close(struct input_dev *input)
{
struct adxl34x *ac = input_get_drvdata(input);
- mutex_lock(&ac->mutex);
+ guard(mutex)(&ac->mutex);
if (!ac->suspended && !ac->disabled)
__adxl34x_disable(ac);
ac->opened = false;
-
- mutex_unlock(&ac->mutex);
}
struct adxl34x *adxl34x_probe(struct device *dev, int irq,
@@ -695,21 +687,21 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
struct adxl34x *ac;
struct input_dev *input_dev;
const struct adxl34x_platform_data *pdata;
- int err, range, i;
- unsigned char revid;
+ int error, range, i;
+ int revid;
if (!irq) {
dev_err(dev, "no IRQ?\n");
- err = -ENODEV;
- goto err_out;
+ return ERR_PTR(-ENODEV);
}
- ac = kzalloc(sizeof(*ac), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ac || !input_dev) {
- err = -ENOMEM;
- goto err_free_mem;
- }
+ ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return ERR_PTR(-ENOMEM);
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev)
+ return ERR_PTR(-ENOMEM);
ac->fifo_delay = fifo_delay_default;
@@ -742,14 +734,12 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
break;
default:
dev_err(dev, "Failed to probe %s\n", input_dev->name);
- err = -ENODEV;
- goto err_free_mem;
+ return ERR_PTR(-ENODEV);
}
snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev));
input_dev->phys = ac->phys;
- input_dev->dev.parent = dev;
input_dev->id.product = ac->model;
input_dev->id.bustype = bops->bustype;
input_dev->open = adxl34x_input_open;
@@ -757,18 +747,12 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
input_set_drvdata(input_dev, ac);
- __set_bit(ac->pdata.ev_type, input_dev->evbit);
-
if (ac->pdata.ev_type == EV_REL) {
- __set_bit(REL_X, input_dev->relbit);
- __set_bit(REL_Y, input_dev->relbit);
- __set_bit(REL_Z, input_dev->relbit);
+ input_set_capability(input_dev, EV_REL, REL_X);
+ input_set_capability(input_dev, EV_REL, REL_Y);
+ input_set_capability(input_dev, EV_REL, REL_Z);
} else {
/* EV_ABS */
- __set_bit(ABS_X, input_dev->absbit);
- __set_bit(ABS_Y, input_dev->absbit);
- __set_bit(ABS_Z, input_dev->absbit);
-
if (pdata->data_range & FULL_RES)
range = ADXL_FULLRES_MAX_VAL; /* Signed 13-bit */
else
@@ -779,18 +763,18 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3);
}
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit);
- __set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit);
- __set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, pdata->ev_code_tap[ADXL_X_AXIS]);
+ input_set_capability(input_dev, EV_KEY, pdata->ev_code_tap[ADXL_Y_AXIS]);
+ input_set_capability(input_dev, EV_KEY, pdata->ev_code_tap[ADXL_Z_AXIS]);
if (pdata->ev_code_ff) {
ac->int_mask = FREE_FALL;
- __set_bit(pdata->ev_code_ff, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, pdata->ev_code_ff);
}
if (pdata->ev_code_act_inactivity)
- __set_bit(pdata->ev_code_act_inactivity, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ pdata->ev_code_act_inactivity);
ac->int_mask |= ACTIVITY | INACTIVITY;
@@ -810,21 +794,16 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
AC_WRITE(ac, POWER_CTL, 0);
- err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
- dev_name(dev), ac);
- if (err) {
+ error = devm_request_threaded_irq(dev, ac->irq, NULL, adxl34x_irq,
+ IRQF_ONESHOT, dev_name(dev), ac);
+ if (error) {
dev_err(dev, "irq %d busy?\n", ac->irq);
- goto err_free_mem;
+ return ERR_PTR(error);
}
- err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group);
- if (err)
- goto err_free_irq;
-
- err = input_register_device(input_dev);
- if (err)
- goto err_remove_attr;
+ error = input_register_device(input_dev);
+ if (error)
+ return ERR_PTR(error);
AC_WRITE(ac, OFSX, pdata->x_axis_offset);
ac->hwcal.x = pdata->x_axis_offset;
@@ -867,13 +846,13 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D)
for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++)
- __set_bit(pdata->ev_codes_orient_3d[i],
- input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ pdata->ev_codes_orient_3d[i]);
if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D)
for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++)
- __set_bit(pdata->ev_codes_orient_2d[i],
- input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ pdata->ev_codes_orient_2d[i]);
} else {
ac->pdata.orientation_enable = 0;
}
@@ -883,30 +862,10 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK);
return ac;
-
- err_remove_attr:
- sysfs_remove_group(&dev->kobj, &adxl34x_attr_group);
- err_free_irq:
- free_irq(ac->irq, ac);
- err_free_mem:
- input_free_device(input_dev);
- kfree(ac);
- err_out:
- return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(adxl34x_probe);
-int adxl34x_remove(struct adxl34x *ac)
-{
- sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group);
- free_irq(ac->irq, ac);
- input_unregister_device(ac->input);
- dev_dbg(ac->dev, "unregistered accelerometer\n");
- kfree(ac);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(adxl34x_remove);
+EXPORT_GPL_SIMPLE_DEV_PM_OPS(adxl34x_pm, adxl34x_suspend, adxl34x_resume);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver");
diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h
index bbbc80fda164..718e90c2046d 100644
--- a/drivers/input/misc/adxl34x.h
+++ b/drivers/input/misc/adxl34x.h
@@ -1,10 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface)
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADXL34X_H_
@@ -20,11 +20,11 @@ struct adxl34x_bus_ops {
int (*write)(struct device *, unsigned char, unsigned char);
};
-void adxl34x_suspend(struct adxl34x *ac);
-void adxl34x_resume(struct adxl34x *ac);
struct adxl34x *adxl34x_probe(struct device *dev, int irq,
bool fifo_delay_default,
const struct adxl34x_bus_ops *bops);
-int adxl34x_remove(struct adxl34x *ac);
+
+extern const struct dev_pm_ops adxl34x_pm;
+extern const struct attribute_group *adxl34x_groups[];
#endif
diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c
index 094bddf56755..d43aebd785b7 100644
--- a/drivers/input/misc/apanel.c
+++ b/drivers/input/misc/apanel.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Fujitsu Lifebook Application Panel button drive
*
* Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
* Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* Many Fujitsu Lifebook laptops have a small panel of buttons that are
* accessible via the i2c/smbus interface. This driver polls those
* buttons and generates input events.
@@ -20,9 +17,8 @@
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/i2c.h>
-#include <linux/workqueue.h>
#include <linux/leds.h>
#define APANEL_NAME "Fujitsu Application Panel"
@@ -55,21 +51,28 @@ static enum apanel_chip device_chip[APANEL_DEV_MAX];
#define MAX_PANEL_KEYS 12
struct apanel {
- struct input_polled_dev *ipdev;
+ struct input_dev *idev;
struct i2c_client *client;
unsigned short keymap[MAX_PANEL_KEYS];
- u16 nkeys;
- u16 led_bits;
- struct work_struct led_work;
+ u16 nkeys;
struct led_classdev mail_led;
};
+static const unsigned short apanel_keymap[MAX_PANEL_KEYS] = {
+ [0] = KEY_MAIL,
+ [1] = KEY_WWW,
+ [2] = KEY_PROG2,
+ [3] = KEY_PROG1,
-static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
+ [8] = KEY_FORWARD,
+ [9] = KEY_REWIND,
+ [10] = KEY_STOPCD,
+ [11] = KEY_PLAYPAUSE,
+};
static void report_key(struct input_dev *input, unsigned keycode)
{
- pr_debug(APANEL ": report key %#x\n", keycode);
+ dev_dbg(input->dev.parent, "report key %#x\n", keycode);
input_report_key(input, keycode, 1);
input_sync(input);
@@ -85,10 +88,9 @@ static void report_key(struct input_dev *input, unsigned keycode)
* CD keys:
* Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
*/
-static void apanel_poll(struct input_polled_dev *ipdev)
+static void apanel_poll(struct input_dev *idev)
{
- struct apanel *ap = ipdev->private;
- struct input_dev *idev = ipdev->input;
+ struct apanel *ap = input_get_drvdata(idev);
u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
s32 data;
int i;
@@ -109,148 +111,101 @@ static void apanel_poll(struct input_polled_dev *ipdev)
report_key(idev, ap->keymap[i]);
}
-/* Track state changes of LED */
-static void led_update(struct work_struct *work)
-{
- struct apanel *ap = container_of(work, struct apanel, led_work);
-
- i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
-}
-
-static void mail_led_set(struct led_classdev *led,
+static int mail_led_set(struct led_classdev *led,
enum led_brightness value)
{
struct apanel *ap = container_of(led, struct apanel, mail_led);
+ u16 led_bits = value != LED_OFF ? 0x8000 : 0x0000;
- if (value != LED_OFF)
- ap->led_bits |= 0x8000;
- else
- ap->led_bits &= ~0x8000;
-
- schedule_work(&ap->led_work);
-}
-
-static int apanel_remove(struct i2c_client *client)
-{
- struct apanel *ap = i2c_get_clientdata(client);
-
- if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
- led_classdev_unregister(&ap->mail_led);
-
- input_unregister_polled_device(ap->ipdev);
- input_free_polled_device(ap->ipdev);
-
- return 0;
-}
-
-static void apanel_shutdown(struct i2c_client *client)
-{
- apanel_remove(client);
+ return i2c_smbus_write_word_data(ap->client, 0x10, led_bits);
}
-static const struct i2c_device_id apanel_id[] = {
- { "fujitsu_apanel", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, apanel_id);
-
-static struct i2c_driver apanel_driver = {
- .driver = {
- .name = APANEL,
- },
- .probe = &apanel_probe,
- .remove = &apanel_remove,
- .shutdown = &apanel_shutdown,
- .id_table = apanel_id,
-};
-
-static struct apanel apanel = {
- .keymap = {
- [0] = KEY_MAIL,
- [1] = KEY_WWW,
- [2] = KEY_PROG2,
- [3] = KEY_PROG1,
-
- [8] = KEY_FORWARD,
- [9] = KEY_REWIND,
- [10] = KEY_STOPCD,
- [11] = KEY_PLAYPAUSE,
-
- },
- .mail_led = {
- .name = "mail:blue",
- .brightness_set = mail_led_set,
- },
-};
-
-/* NB: Only one panel on the i2c. */
-static int apanel_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int apanel_probe(struct i2c_client *client)
{
struct apanel *ap;
- struct input_polled_dev *ipdev;
struct input_dev *idev;
u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
- int i, err = -ENOMEM;
+ int i, err;
- ap = &apanel;
+ ap = devm_kzalloc(&client->dev, sizeof(*ap), GFP_KERNEL);
+ if (!ap)
+ return -ENOMEM;
- ipdev = input_allocate_polled_device();
- if (!ipdev)
- goto out1;
+ idev = devm_input_allocate_device(&client->dev);
+ if (!idev)
+ return -ENOMEM;
- ap->ipdev = ipdev;
+ ap->idev = idev;
ap->client = client;
i2c_set_clientdata(client, ap);
err = i2c_smbus_write_word_data(client, cmd, 0);
if (err) {
- dev_warn(&client->dev, APANEL ": smbus write error %d\n",
- err);
- goto out3;
+ dev_warn(&client->dev, "smbus write error %d\n", err);
+ return err;
}
- ipdev->poll = apanel_poll;
- ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
- ipdev->private = ap;
+ input_set_drvdata(idev, ap);
- idev = ipdev->input;
idev->name = APANEL_NAME " buttons";
idev->phys = "apanel/input0";
idev->id.bustype = BUS_HOST;
- idev->dev.parent = &client->dev;
-
- set_bit(EV_KEY, idev->evbit);
+ memcpy(ap->keymap, apanel_keymap, sizeof(apanel_keymap));
idev->keycode = ap->keymap;
idev->keycodesize = sizeof(ap->keymap[0]);
idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
+ set_bit(EV_KEY, idev->evbit);
for (i = 0; i < idev->keycodemax; i++)
if (ap->keymap[i])
set_bit(ap->keymap[i], idev->keybit);
- err = input_register_polled_device(ipdev);
+ err = input_setup_polling(idev, apanel_poll);
+ if (err)
+ return err;
+
+ input_set_poll_interval(idev, POLL_INTERVAL_DEFAULT);
+
+ err = input_register_device(idev);
if (err)
- goto out3;
+ return err;
- INIT_WORK(&ap->led_work, led_update);
if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
- err = led_classdev_register(&client->dev, &ap->mail_led);
+ ap->mail_led.name = "mail:blue";
+ ap->mail_led.brightness_set_blocking = mail_led_set;
+ err = devm_led_classdev_register(&client->dev, &ap->mail_led);
if (err)
- goto out4;
+ return err;
}
return 0;
-out4:
- input_unregister_polled_device(ipdev);
-out3:
- input_free_polled_device(ipdev);
-out1:
- return err;
}
+static void apanel_shutdown(struct i2c_client *client)
+{
+ struct apanel *ap = i2c_get_clientdata(client);
+
+ if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+ led_set_brightness(&ap->mail_led, LED_OFF);
+}
+
+static const struct i2c_device_id apanel_id[] = {
+ { "fujitsu_apanel" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, apanel_id);
+
+static struct i2c_driver apanel_driver = {
+ .driver = {
+ .name = APANEL,
+ },
+ .probe = apanel_probe,
+ .shutdown = apanel_shutdown,
+ .id_table = apanel_id,
+};
+
/* Scan the system ROM for the signature "FJKEYINF" */
static __init const void __iomem *bios_signature(const void __iomem *bios)
{
diff --git a/drivers/input/misc/ariel-pwrbutton.c b/drivers/input/misc/ariel-pwrbutton.c
new file mode 100644
index 000000000000..cdc80715b5fd
--- /dev/null
+++ b/drivers/input/misc/ariel-pwrbutton.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
+/*
+ * Dell Wyse 3020 a.k.a. "Ariel" Power Button Driver
+ *
+ * Copyright (C) 2020 Lubomir Rintel
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#define RESP_COUNTER(response) (response.header & 0x3)
+#define RESP_SIZE(response) ((response.header >> 2) & 0x3)
+#define RESP_TYPE(response) ((response.header >> 4) & 0xf)
+
+struct ec_input_response {
+ u8 reserved;
+ u8 header;
+ u8 data[3];
+} __packed;
+
+struct ariel_pwrbutton {
+ struct spi_device *client;
+ struct input_dev *input;
+ u8 msg_counter;
+};
+
+static int ec_input_read(struct ariel_pwrbutton *priv,
+ struct ec_input_response *response)
+{
+ u8 read_request[] = { 0x00, 0x5a, 0xa5, 0x00, 0x00 };
+ struct spi_device *spi = priv->client;
+ struct spi_transfer t = {
+ .tx_buf = read_request,
+ .rx_buf = response,
+ .len = sizeof(read_request),
+ };
+
+ compiletime_assert(sizeof(read_request) == sizeof(*response),
+ "SPI xfer request/response size mismatch");
+
+ return spi_sync_transfer(spi, &t, 1);
+}
+
+static irqreturn_t ec_input_interrupt(int irq, void *dev_id)
+{
+ struct ariel_pwrbutton *priv = dev_id;
+ struct spi_device *spi = priv->client;
+ struct ec_input_response response;
+ int error;
+ int i;
+
+ error = ec_input_read(priv, &response);
+ if (error < 0) {
+ dev_err(&spi->dev, "EC read failed: %d\n", error);
+ goto out;
+ }
+
+ if (priv->msg_counter == RESP_COUNTER(response)) {
+ dev_warn(&spi->dev, "No new data to read?\n");
+ goto out;
+ }
+
+ priv->msg_counter = RESP_COUNTER(response);
+
+ if (RESP_TYPE(response) != 0x3 && RESP_TYPE(response) != 0xc) {
+ dev_dbg(&spi->dev, "Ignoring message that's not kbd data\n");
+ goto out;
+ }
+
+ for (i = 0; i < RESP_SIZE(response); i++) {
+ switch (response.data[i]) {
+ case 0x74:
+ input_report_key(priv->input, KEY_POWER, 1);
+ input_sync(priv->input);
+ break;
+ case 0xf4:
+ input_report_key(priv->input, KEY_POWER, 0);
+ input_sync(priv->input);
+ break;
+ default:
+ dev_dbg(&spi->dev, "Unknown scan code: %02x\n",
+ response.data[i]);
+ }
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int ariel_pwrbutton_probe(struct spi_device *spi)
+{
+ struct ec_input_response response;
+ struct ariel_pwrbutton *priv;
+ int error;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "Missing IRQ.\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = spi;
+ spi_set_drvdata(spi, priv);
+
+ priv->input = devm_input_allocate_device(&spi->dev);
+ if (!priv->input)
+ return -ENOMEM;
+ priv->input->name = "Power Button";
+ priv->input->dev.parent = &spi->dev;
+ input_set_capability(priv->input, EV_KEY, KEY_POWER);
+ error = input_register_device(priv->input);
+ if (error) {
+ dev_err(&spi->dev, "error registering input device: %d\n", error);
+ return error;
+ }
+
+ error = ec_input_read(priv, &response);
+ if (error < 0) {
+ dev_err(&spi->dev, "EC read failed: %d\n", error);
+ return error;
+ }
+ priv->msg_counter = RESP_COUNTER(response);
+
+ error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
+ ec_input_interrupt,
+ IRQF_ONESHOT,
+ "Ariel EC Input", priv);
+
+ if (error) {
+ dev_err(&spi->dev, "Failed to request IRQ %d: %d\n",
+ spi->irq, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ariel_pwrbutton_of_match[] = {
+ { .compatible = "dell,wyse-ariel-ec-input" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match);
+
+static const struct spi_device_id ariel_pwrbutton_spi_ids[] = {
+ { .name = "wyse-ariel-ec-input" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids);
+
+static struct spi_driver ariel_pwrbutton_driver = {
+ .driver = {
+ .name = "dell-wyse-ariel-ec-input",
+ .of_match_table = ariel_pwrbutton_of_match,
+ },
+ .probe = ariel_pwrbutton_probe,
+ .id_table = ariel_pwrbutton_spi_ids,
+};
+module_spi_driver(ariel_pwrbutton_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("Dell Wyse 3020 Power Button Input Driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
index 21dc1b8b2a4a..bb1544d63c51 100644
--- a/drivers/input/misc/arizona-haptics.c
+++ b/drivers/input/misc/arizona-haptics.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Arizona haptics driver
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -37,8 +34,6 @@ static void arizona_haptics_work(struct work_struct *work)
struct arizona_haptics,
work);
struct arizona *arizona = haptics->arizona;
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(arizona->dapm);
int ret;
if (!haptics->arizona->dapm) {
@@ -68,7 +63,7 @@ static void arizona_haptics_work(struct work_struct *work)
return;
}
- ret = snd_soc_component_enable_pin(component, "HAPTICS");
+ ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
ret);
@@ -83,7 +78,7 @@ static void arizona_haptics_work(struct work_struct *work)
}
} else {
/* This disable sequence will be a noop if already enabled */
- ret = snd_soc_component_disable_pin(component, "HAPTICS");
+ ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
ret);
@@ -142,14 +137,12 @@ static int arizona_haptics_play(struct input_dev *input, void *data,
static void arizona_haptics_close(struct input_dev *input)
{
struct arizona_haptics *haptics = input_get_drvdata(input);
- struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm = haptics->arizona->dapm;
cancel_work_sync(&haptics->work);
- if (haptics->arizona->dapm) {
- component = snd_soc_dapm_to_component(haptics->arizona->dapm);
- snd_soc_component_disable_pin(component, "HAPTICS");
- }
+ if (dapm)
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
}
static int arizona_haptics_probe(struct platform_device *pdev)
diff --git a/drivers/input/misc/atc260x-onkey.c b/drivers/input/misc/atc260x-onkey.c
new file mode 100644
index 000000000000..999aabf9dcbd
--- /dev/null
+++ b/drivers/input/misc/atc260x-onkey.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Onkey driver for Actions Semi ATC260x PMICs.
+ *
+ * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/atc260x/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* <2s for short press, >2s for long press */
+#define KEY_PRESS_TIME_SEC 2
+
+/* Driver internals */
+enum atc260x_onkey_reset_status {
+ KEY_RESET_HW_DEFAULT,
+ KEY_RESET_DISABLED,
+ KEY_RESET_USER_SEL,
+};
+
+struct atc260x_onkey_params {
+ u32 reg_int_ctl;
+ u32 kdwn_state_bm;
+ u32 long_int_pnd_bm;
+ u32 short_int_pnd_bm;
+ u32 kdwn_int_pnd_bm;
+ u32 press_int_en_bm;
+ u32 kdwn_int_en_bm;
+ u32 press_time_bm;
+ u32 reset_en_bm;
+ u32 reset_time_bm;
+};
+
+struct atc260x_onkey {
+ struct atc260x *atc260x;
+ const struct atc260x_onkey_params *params;
+ struct input_dev *input_dev;
+ struct delayed_work work;
+ int irq;
+};
+
+static const struct atc260x_onkey_params atc2603c_onkey_params = {
+ .reg_int_ctl = ATC2603C_PMU_SYS_CTL2,
+ .long_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
+ .short_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
+ .kdwn_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD,
+ .press_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN,
+ .kdwn_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
+ .kdwn_state_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS,
+ .press_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
+ .reset_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN,
+ .reset_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
+};
+
+static const struct atc260x_onkey_params atc2609a_onkey_params = {
+ .reg_int_ctl = ATC2609A_PMU_SYS_CTL2,
+ .long_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
+ .short_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
+ .kdwn_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD,
+ .press_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN,
+ .kdwn_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
+ .kdwn_state_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS,
+ .press_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
+ .reset_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN,
+ .reset_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
+};
+
+static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey,
+ enum atc260x_onkey_reset_status reset_status,
+ u32 reset_time, u32 press_time)
+{
+ u32 reg_bm, reg_val;
+
+ reg_bm = onkey->params->long_int_pnd_bm |
+ onkey->params->short_int_pnd_bm |
+ onkey->params->kdwn_int_pnd_bm |
+ onkey->params->press_int_en_bm |
+ onkey->params->kdwn_int_en_bm;
+
+ reg_val = reg_bm | press_time;
+ reg_bm |= onkey->params->press_time_bm;
+
+ if (reset_status == KEY_RESET_DISABLED) {
+ reg_bm |= onkey->params->reset_en_bm;
+ } else if (reset_status == KEY_RESET_USER_SEL) {
+ reg_bm |= onkey->params->reset_en_bm |
+ onkey->params->reset_time_bm;
+ reg_val |= onkey->params->reset_en_bm | reset_time;
+ }
+
+ return regmap_update_bits(onkey->atc260x->regmap,
+ onkey->params->reg_int_ctl, reg_bm, reg_val);
+}
+
+static void atc260x_onkey_query(struct atc260x_onkey *onkey)
+{
+ u32 reg_bits;
+ int ret, key_down;
+
+ ret = regmap_read(onkey->atc260x->regmap,
+ onkey->params->reg_int_ctl, &key_down);
+ if (ret) {
+ key_down = 1;
+ dev_err(onkey->atc260x->dev,
+ "Failed to read onkey status: %d\n", ret);
+ } else {
+ key_down &= onkey->params->kdwn_state_bm;
+ }
+
+ /*
+ * The hardware generates interrupt only when the onkey pin is
+ * asserted. Hence, the deassertion of the pin is simulated through
+ * work queue.
+ */
+ if (key_down) {
+ schedule_delayed_work(&onkey->work, msecs_to_jiffies(200));
+ return;
+ }
+
+ /*
+ * The key-down status bit is cleared when the On/Off button
+ * is released.
+ */
+ input_report_key(onkey->input_dev, KEY_POWER, 0);
+ input_sync(onkey->input_dev);
+
+ reg_bits = onkey->params->long_int_pnd_bm |
+ onkey->params->short_int_pnd_bm |
+ onkey->params->kdwn_int_pnd_bm |
+ onkey->params->press_int_en_bm |
+ onkey->params->kdwn_int_en_bm;
+
+ /* Clear key press pending events and enable key press interrupts. */
+ regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl,
+ reg_bits, reg_bits);
+}
+
+static void atc260x_onkey_work(struct work_struct *work)
+{
+ struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey,
+ work.work);
+ atc260x_onkey_query(onkey);
+}
+
+static irqreturn_t atc260x_onkey_irq(int irq, void *data)
+{
+ struct atc260x_onkey *onkey = data;
+ int ret;
+
+ /* Disable key press interrupts. */
+ ret = regmap_update_bits(onkey->atc260x->regmap,
+ onkey->params->reg_int_ctl,
+ onkey->params->press_int_en_bm |
+ onkey->params->kdwn_int_en_bm, 0);
+ if (ret)
+ dev_err(onkey->atc260x->dev,
+ "Failed to disable interrupts: %d\n", ret);
+
+ input_report_key(onkey->input_dev, KEY_POWER, 1);
+ input_sync(onkey->input_dev);
+
+ atc260x_onkey_query(onkey);
+
+ return IRQ_HANDLED;
+}
+
+static int atc260x_onkey_open(struct input_dev *dev)
+{
+ struct atc260x_onkey *onkey = input_get_drvdata(dev);
+
+ enable_irq(onkey->irq);
+
+ return 0;
+}
+
+static void atc260x_onkey_close(struct input_dev *dev)
+{
+ struct atc260x_onkey *onkey = input_get_drvdata(dev);
+
+ disable_irq(onkey->irq);
+ cancel_delayed_work_sync(&onkey->work);
+}
+
+static int atc260x_onkey_probe(struct platform_device *pdev)
+{
+ struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
+ struct atc260x_onkey *onkey;
+ struct input_dev *input_dev;
+ enum atc260x_onkey_reset_status reset_status;
+ u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0;
+ int val, error;
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ error = device_property_read_u32(pdev->dev.parent,
+ "reset-time-sec", &val);
+ if (error) {
+ reset_status = KEY_RESET_HW_DEFAULT;
+ } else if (val) {
+ if (val < 6 || val > 12) {
+ dev_err(&pdev->dev, "reset-time-sec out of range\n");
+ return -EINVAL;
+ }
+
+ reset_status = KEY_RESET_USER_SEL;
+ reset_time = (val - 6) / 2;
+ } else {
+ reset_status = KEY_RESET_DISABLED;
+ dev_dbg(&pdev->dev, "Disabled reset on long-press\n");
+ }
+
+ switch (atc260x->ic_type) {
+ case ATC2603C:
+ onkey->params = &atc2603c_onkey_params;
+ press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
+ press_time);
+ reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
+ reset_time);
+ break;
+ case ATC2609A:
+ onkey->params = &atc2609a_onkey_params;
+ press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
+ press_time);
+ reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
+ reset_time);
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "OnKey not supported for ATC260x PMIC type: %u\n",
+ atc260x->ic_type);
+ return -EINVAL;
+ }
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev) {
+ dev_err(&pdev->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ onkey->input_dev = input_dev;
+ onkey->atc260x = atc260x;
+
+ input_dev->name = "atc260x-onkey";
+ input_dev->phys = "atc260x-onkey/input0";
+ input_dev->open = atc260x_onkey_open;
+ input_dev->close = atc260x_onkey_close;
+
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+ input_set_drvdata(input_dev, onkey);
+
+ INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work);
+
+ onkey->irq = platform_get_irq(pdev, 0);
+ if (onkey->irq < 0)
+ return onkey->irq;
+
+ error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL,
+ atc260x_onkey_irq, IRQF_ONESHOT,
+ dev_name(&pdev->dev), onkey);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to register IRQ %d: %d\n", onkey->irq, error);
+ return error;
+ }
+
+ /* Keep IRQ disabled until atc260x_onkey_open() is called. */
+ disable_irq(onkey->irq);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ error = atc2603x_onkey_hw_init(onkey, reset_status,
+ reset_time, press_time);
+ if (error)
+ return error;
+
+ device_init_wakeup(&pdev->dev, true);
+
+ return 0;
+}
+
+static struct platform_driver atc260x_onkey_driver = {
+ .probe = atc260x_onkey_probe,
+ .driver = {
+ .name = "atc260x-onkey",
+ },
+};
+
+module_platform_driver(atc260x_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index d8fd58fdf050..e84649af801d 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ati_remote2 - ATI/Philips USB RF remote driver
*
* Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
* Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
*/
#include <linux/usb/input.h>
@@ -71,7 +68,7 @@ static int ati_remote2_get_channel_mask(char *buffer,
{
pr_debug("%s()\n", __func__);
- return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
+ return sprintf(buffer, "0x%04x\n", *(unsigned int *)kp->arg);
}
static int ati_remote2_set_mode_mask(const char *val,
@@ -87,7 +84,7 @@ static int ati_remote2_get_mode_mask(char *buffer,
{
pr_debug("%s()\n", __func__);
- return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
+ return sprintf(buffer, "0x%02x\n", *(unsigned int *)kp->arg);
}
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
@@ -207,26 +204,7 @@ struct ati_remote2 {
unsigned int mode_mask;
};
-static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
-static void ati_remote2_disconnect(struct usb_interface *interface);
-static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
-static int ati_remote2_resume(struct usb_interface *interface);
-static int ati_remote2_reset_resume(struct usb_interface *interface);
-static int ati_remote2_pre_reset(struct usb_interface *interface);
-static int ati_remote2_post_reset(struct usb_interface *interface);
-
-static struct usb_driver ati_remote2_driver = {
- .name = "ati_remote2",
- .probe = ati_remote2_probe,
- .disconnect = ati_remote2_disconnect,
- .id_table = ati_remote2_id_table,
- .suspend = ati_remote2_suspend,
- .resume = ati_remote2_resume,
- .reset_resume = ati_remote2_reset_resume,
- .pre_reset = ati_remote2_pre_reset,
- .post_reset = ati_remote2_post_reset,
- .supports_autosuspend = 1,
-};
+static struct usb_driver ati_remote2_driver;
static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
{
@@ -266,29 +244,21 @@ static int ati_remote2_open(struct input_dev *idev)
if (r) {
dev_err(&ar2->intf[0]->dev,
"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
- goto fail1;
+ return r;
}
- mutex_lock(&ati_remote2_mutex);
+ scoped_guard(mutex, &ati_remote2_mutex) {
+ if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
+ r = ati_remote2_submit_urbs(ar2);
+ if (r)
+ break;
+ }
- if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
- r = ati_remote2_submit_urbs(ar2);
- if (r)
- goto fail2;
+ ar2->flags |= ATI_REMOTE2_OPENED;
}
- ar2->flags |= ATI_REMOTE2_OPENED;
-
- mutex_unlock(&ati_remote2_mutex);
-
usb_autopm_put_interface(ar2->intf[0]);
- return 0;
-
- fail2:
- mutex_unlock(&ati_remote2_mutex);
- usb_autopm_put_interface(ar2->intf[0]);
- fail1:
return r;
}
@@ -298,14 +268,12 @@ static void ati_remote2_close(struct input_dev *idev)
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
- mutex_lock(&ati_remote2_mutex);
+ guard(mutex)(&ati_remote2_mutex);
if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
ati_remote2_kill_urbs(ar2);
ar2->flags &= ~ATI_REMOTE2_OPENED;
-
- mutex_unlock(&ati_remote2_mutex);
}
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
@@ -642,7 +610,7 @@ static int ati_remote2_urb_init(struct ati_remote2 *ar2)
return -ENOMEM;
pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
- maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = usb_maxpacket(udev, pipe);
maxp = maxp > 4 ? 4 : maxp;
usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
@@ -735,16 +703,14 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev,
return r;
}
- mutex_lock(&ati_remote2_mutex);
-
- if (mask != ar2->channel_mask) {
- r = ati_remote2_setup(ar2, mask);
- if (!r)
- ar2->channel_mask = mask;
+ scoped_guard(mutex, &ati_remote2_mutex) {
+ if (mask != ar2->channel_mask) {
+ r = ati_remote2_setup(ar2, mask);
+ if (!r)
+ ar2->channel_mask = mask;
+ }
}
- mutex_unlock(&ati_remote2_mutex);
-
usb_autopm_put_interface(ar2->intf[0]);
return r ? r : count;
@@ -794,10 +760,7 @@ static struct attribute *ati_remote2_attrs[] = {
&dev_attr_mode_mask.attr,
NULL,
};
-
-static struct attribute_group ati_remote2_attr_group = {
- .attrs = ati_remote2_attrs,
-};
+ATTRIBUTE_GROUPS(ati_remote2);
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
@@ -864,13 +827,9 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
- r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
- if (r)
- goto fail3;
-
r = ati_remote2_input_init(ar2);
if (r)
- goto fail4;
+ goto fail3;
usb_set_intfdata(interface, ar2);
@@ -878,8 +837,6 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
return 0;
- fail4:
- sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
fail3:
ati_remote2_urb_cleanup(ar2);
fail2:
@@ -903,8 +860,6 @@ static void ati_remote2_disconnect(struct usb_interface *interface)
input_unregister_device(ar2->idev);
- sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
-
ati_remote2_urb_cleanup(ar2);
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
@@ -925,15 +880,13 @@ static int ati_remote2_suspend(struct usb_interface *interface,
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
- mutex_lock(&ati_remote2_mutex);
+ guard(mutex)(&ati_remote2_mutex);
if (ar2->flags & ATI_REMOTE2_OPENED)
ati_remote2_kill_urbs(ar2);
ar2->flags |= ATI_REMOTE2_SUSPENDED;
- mutex_unlock(&ati_remote2_mutex);
-
return 0;
}
@@ -950,7 +903,7 @@ static int ati_remote2_resume(struct usb_interface *interface)
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
- mutex_lock(&ati_remote2_mutex);
+ guard(mutex)(&ati_remote2_mutex);
if (ar2->flags & ATI_REMOTE2_OPENED)
r = ati_remote2_submit_urbs(ar2);
@@ -958,8 +911,6 @@ static int ati_remote2_resume(struct usb_interface *interface)
if (!r)
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
- mutex_unlock(&ati_remote2_mutex);
-
return r;
}
@@ -976,11 +927,11 @@ static int ati_remote2_reset_resume(struct usb_interface *interface)
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
- mutex_lock(&ati_remote2_mutex);
+ guard(mutex)(&ati_remote2_mutex);
r = ati_remote2_setup(ar2, ar2->channel_mask);
if (r)
- goto out;
+ return r;
if (ar2->flags & ATI_REMOTE2_OPENED)
r = ati_remote2_submit_urbs(ar2);
@@ -988,9 +939,6 @@ static int ati_remote2_reset_resume(struct usb_interface *interface)
if (!r)
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
- out:
- mutex_unlock(&ati_remote2_mutex);
-
return r;
}
@@ -1035,4 +983,18 @@ static int ati_remote2_post_reset(struct usb_interface *interface)
return r;
}
+static struct usb_driver ati_remote2_driver = {
+ .name = "ati_remote2",
+ .probe = ati_remote2_probe,
+ .disconnect = ati_remote2_disconnect,
+ .dev_groups = ati_remote2_groups,
+ .id_table = ati_remote2_id_table,
+ .suspend = ati_remote2_suspend,
+ .resume = ati_remote2_resume,
+ .reset_resume = ati_remote2_reset_resume,
+ .pre_reset = ati_remote2_pre_reset,
+ .post_reset = ati_remote2_post_reset,
+ .supports_autosuspend = 1,
+};
+
module_usb_driver(ati_remote2_driver);
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
index 6423aaccc763..5b9be2957746 100644
--- a/drivers/input/misc/atlas_btns.c
+++ b/drivers/input/misc/atlas_btns.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras
*
* Copyright (C) 2006 Jaya Kumar
* Based on Toshiba ACPI by John Belmonte and ASUS ACPI
* This work was sponsored by CIS(M) Sdn Bhd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -120,7 +106,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
return err;
}
-static int atlas_acpi_button_remove(struct acpi_device *device)
+static void atlas_acpi_button_remove(struct acpi_device *device)
{
acpi_status status;
@@ -130,8 +116,6 @@ static int atlas_acpi_button_remove(struct acpi_device *device)
pr_err("error removing addr spc handler\n");
input_unregister_device(input_dev);
-
- return 0;
}
static const struct acpi_device_id atlas_device_ids[] = {
@@ -143,7 +127,6 @@ MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
static struct acpi_driver atlas_acpi_driver = {
.name = ACPI_ATLAS_NAME,
.class = ACPI_ATLAS_CLASS,
- .owner = THIS_MODULE,
.ids = atlas_device_ids,
.ops = {
.add = atlas_acpi_button_add,
diff --git a/drivers/input/misc/atmel_captouch.c b/drivers/input/misc/atmel_captouch.c
index c4c0f4bb7627..f9744cf0ca80 100644
--- a/drivers/input/misc/atmel_captouch.c
+++ b/drivers/input/misc/atmel_captouch.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Atmel Atmegaxx Capacitive Touch Button Driver
*
* Copyright (C) 2016 Google, inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
@@ -169,8 +161,7 @@ out:
/*
* Probe function to setup the device, input system and interrupt
*/
-static int atmel_captouch_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int atmel_captouch_probe(struct i2c_client *client)
{
struct atmel_captouch_device *capdev;
struct device *dev = &client->dev;
@@ -257,7 +248,6 @@ static int atmel_captouch_probe(struct i2c_client *client,
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id atmel_captouch_of_id[] = {
{
.compatible = "atmel,captouch",
@@ -265,10 +255,9 @@ static const struct of_device_id atmel_captouch_of_id[] = {
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, atmel_captouch_of_id);
-#endif
static const struct i2c_device_id atmel_captouch_id[] = {
- { "atmel_captouch", 0 },
+ { "atmel_captouch" },
{ }
};
MODULE_DEVICE_TABLE(i2c, atmel_captouch_id);
@@ -278,7 +267,7 @@ static struct i2c_driver atmel_captouch_driver = {
.id_table = atmel_captouch_id,
.driver = {
.name = "atmel_captouch",
- .of_match_table = of_match_ptr(atmel_captouch_of_id),
+ .of_match_table = atmel_captouch_of_id,
},
};
module_i2c_driver(atmel_captouch_driver);
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, &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/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
index debeeaeb8812..24f9e9d893de 100644
--- a/drivers/input/misc/axp20x-pek.c
+++ b/drivers/input/misc/axp20x-pek.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
+#include <linux/platform_data/x86/soc.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -132,20 +133,11 @@ static ssize_t axp20x_store_attr(struct device *dev,
size_t count)
{
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
- char val_str[20];
- size_t len;
int ret, i;
unsigned int val, idx = 0;
unsigned int best_err = UINT_MAX;
- val_str[sizeof(val_str) - 1] = '\0';
- strncpy(val_str, buf, sizeof(val_str) - 1);
- len = strlen(val_str);
-
- if (len && val_str[len - 1] == '\n')
- val_str[len - 1] = '\0';
-
- ret = kstrtouint(val_str, 10, &val);
+ ret = kstrtouint(buf, 10, &val);
if (ret)
return ret;
@@ -191,19 +183,17 @@ static ssize_t axp20x_store_attr_shutdown(struct device *dev,
axp20x_pek->info->shutdown_mask, buf, count);
}
-DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, axp20x_store_attr_startup);
-DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown,
- axp20x_store_attr_shutdown);
+static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup,
+ axp20x_store_attr_startup);
+static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown,
+ axp20x_store_attr_shutdown);
-static struct attribute *axp20x_attributes[] = {
+static struct attribute *axp20x_attrs[] = {
&dev_attr_startup.attr,
&dev_attr_shutdown.attr,
NULL,
};
-
-static const struct attribute_group axp20x_attribute_group = {
- .attrs = axp20x_attributes,
-};
+ATTRIBUTE_GROUPS(axp20x);
static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
{
@@ -232,20 +222,14 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
int error;
axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
- if (axp20x_pek->irq_dbr < 0) {
- dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
- axp20x_pek->irq_dbr);
+ if (axp20x_pek->irq_dbr < 0)
return axp20x_pek->irq_dbr;
- }
axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
axp20x_pek->irq_dbr);
axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
- if (axp20x_pek->irq_dbf < 0) {
- dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
- axp20x_pek->irq_dbf);
+ if (axp20x_pek->irq_dbf < 0)
return axp20x_pek->irq_dbf;
- }
axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
axp20x_pek->irq_dbf);
@@ -288,47 +272,29 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
return error;
}
- if (axp20x_pek->axp20x->variant == AXP288_ID)
- enable_irq_wake(axp20x_pek->irq_dbr);
+ device_init_wakeup(&pdev->dev, true);
return 0;
}
-#ifdef CONFIG_ACPI
-static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
- struct platform_device *pdev)
+static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek)
{
- unsigned long long hrv = 0;
- acpi_status status;
-
if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) &&
axp20x_pek->axp20x->variant == AXP288_ID) {
- status = acpi_evaluate_integer(ACPI_HANDLE(pdev->dev.parent),
- "_HRV", NULL, &hrv);
- if (ACPI_FAILURE(status))
- dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n");
-
/*
* On Cherry Trail platforms (hrv == 3), do not register the
* input device if there is an "INTCFD9" or "ACPI0011" gpio
* button ACPI device, as that handles the power button too,
* and otherwise we end up reporting all presses twice.
*/
- if (hrv == 3 && (acpi_dev_present("INTCFD9", NULL, -1) ||
+ if (soc_intel_is_cht() &&
+ (acpi_dev_present("INTCFD9", NULL, -1) ||
acpi_dev_present("ACPI0011", NULL, -1)))
return false;
-
}
return true;
}
-#else
-static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
- struct platform_device *pdev)
-{
- return true;
-}
-#endif
static int axp20x_pek_probe(struct platform_device *pdev)
{
@@ -348,7 +314,7 @@ static int axp20x_pek_probe(struct platform_device *pdev)
axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
- if (axp20x_pek_should_register_input(axp20x_pek, pdev)) {
+ if (axp20x_pek_should_register_input(axp20x_pek)) {
error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
if (error)
return error;
@@ -356,14 +322,41 @@ static int axp20x_pek_probe(struct platform_device *pdev)
axp20x_pek->info = (struct axp20x_info *)match->driver_data;
- error = devm_device_add_group(&pdev->dev, &axp20x_attribute_group);
- if (error) {
- dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n",
- error);
- return error;
+ platform_set_drvdata(pdev, axp20x_pek);
+
+ return 0;
+}
+
+static int axp20x_pek_suspend(struct device *dev)
+{
+ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+
+ /*
+ * As nested threaded IRQs are not automatically disabled during
+ * suspend, we must explicitly disable non-wakeup IRQs.
+ */
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(axp20x_pek->irq_dbf);
+ enable_irq_wake(axp20x_pek->irq_dbr);
+ } else {
+ disable_irq(axp20x_pek->irq_dbf);
+ disable_irq(axp20x_pek->irq_dbr);
}
- platform_set_drvdata(pdev, axp20x_pek);
+ return 0;
+}
+
+static int axp20x_pek_resume(struct device *dev)
+{
+ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(axp20x_pek->irq_dbf);
+ disable_irq_wake(axp20x_pek->irq_dbr);
+ } else {
+ enable_irq(axp20x_pek->irq_dbf);
+ enable_irq(axp20x_pek->irq_dbr);
+ }
return 0;
}
@@ -387,9 +380,8 @@ static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev)
}
static const struct dev_pm_ops axp20x_pek_pm_ops = {
-#ifdef CONFIG_PM_SLEEP
- .resume_noirq = axp20x_pek_resume_noirq,
-#endif
+ SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume)
+ .resume_noirq = pm_sleep_ptr(axp20x_pek_resume_noirq),
};
static const struct platform_device_id axp_pek_id_match[] = {
@@ -410,7 +402,8 @@ static struct platform_driver axp20x_pek_driver = {
.id_table = axp_pek_id_match,
.driver = {
.name = "axp20x-pek",
- .pm = &axp20x_pek_pm_ops,
+ .pm = pm_sleep_ptr(&axp20x_pek_pm_ops),
+ .dev_groups = axp20x_groups,
},
};
module_platform_driver(axp20x_pek_driver);
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 1efcfdf9f8a8..4cc2a0dcaa75 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2011 Bosch Sensortec GmbH
* Copyright (c) 2011 Unixphere
@@ -8,26 +9,11 @@
*
* The datasheet for the BMA150 chip can be found here:
* http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
-#include <linux/input-polldev.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -136,7 +122,6 @@
struct bma150_data {
struct i2c_client *client;
- struct input_polled_dev *input_polled;
struct input_dev *input;
u8 mode;
};
@@ -349,13 +334,16 @@ static irqreturn_t bma150_irq_thread(int irq, void *dev)
return IRQ_HANDLED;
}
-static void bma150_poll(struct input_polled_dev *dev)
+static void bma150_poll(struct input_dev *input)
{
- bma150_report_xyz(dev->private);
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
+ bma150_report_xyz(bma150);
}
-static int bma150_open(struct bma150_data *bma150)
+static int bma150_open(struct input_dev *input)
{
+ struct bma150_data *bma150 = input_get_drvdata(input);
int error;
error = pm_runtime_get_sync(&bma150->client->dev);
@@ -375,44 +363,18 @@ static int bma150_open(struct bma150_data *bma150)
return 0;
}
-static void bma150_close(struct bma150_data *bma150)
+static void bma150_close(struct input_dev *input)
{
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
pm_runtime_put_sync(&bma150->client->dev);
if (bma150->mode != BMA150_MODE_SLEEP)
bma150_set_mode(bma150, BMA150_MODE_SLEEP);
}
-static int bma150_irq_open(struct input_dev *input)
-{
- struct bma150_data *bma150 = input_get_drvdata(input);
-
- return bma150_open(bma150);
-}
-
-static void bma150_irq_close(struct input_dev *input)
-{
- struct bma150_data *bma150 = input_get_drvdata(input);
-
- bma150_close(bma150);
-}
-
-static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
-{
- struct bma150_data *bma150 = ipoll_dev->private;
-
- bma150_open(bma150);
-}
-
-static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
-{
- struct bma150_data *bma150 = ipoll_dev->private;
-
- bma150_close(bma150);
-}
-
static int bma150_initialize(struct bma150_data *bma150,
- const struct bma150_cfg *cfg)
+ const struct bma150_cfg *cfg)
{
int error;
@@ -452,83 +414,13 @@ static int bma150_initialize(struct bma150_data *bma150,
return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
}
-static void bma150_init_input_device(struct bma150_data *bma150,
- struct input_dev *idev)
-{
- idev->name = BMA150_DRIVER;
- idev->phys = BMA150_DRIVER "/input0";
- idev->id.bustype = BUS_I2C;
- idev->dev.parent = &bma150->client->dev;
-
- idev->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
- input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
- input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
-}
-
-static int bma150_register_input_device(struct bma150_data *bma150)
-{
- struct input_dev *idev;
- int error;
-
- idev = input_allocate_device();
- if (!idev)
- return -ENOMEM;
-
- bma150_init_input_device(bma150, idev);
-
- idev->open = bma150_irq_open;
- idev->close = bma150_irq_close;
- input_set_drvdata(idev, bma150);
-
- error = input_register_device(idev);
- if (error) {
- input_free_device(idev);
- return error;
- }
-
- bma150->input = idev;
- return 0;
-}
-
-static int bma150_register_polled_device(struct bma150_data *bma150)
-{
- struct input_polled_dev *ipoll_dev;
- int error;
-
- ipoll_dev = input_allocate_polled_device();
- if (!ipoll_dev)
- return -ENOMEM;
-
- ipoll_dev->private = bma150;
- ipoll_dev->open = bma150_poll_open;
- ipoll_dev->close = bma150_poll_close;
- ipoll_dev->poll = bma150_poll;
- ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
- ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
- ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
-
- bma150_init_input_device(bma150, ipoll_dev->input);
-
- error = input_register_polled_device(ipoll_dev);
- if (error) {
- input_free_polled_device(ipoll_dev);
- return error;
- }
-
- bma150->input_polled = ipoll_dev;
- bma150->input = ipoll_dev->input;
-
- return 0;
-}
-
-static int bma150_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int bma150_probe(struct i2c_client *client)
{
const struct bma150_platform_data *pdata =
dev_get_platdata(&client->dev);
const struct bma150_cfg *cfg;
struct bma150_data *bma150;
+ struct input_dev *idev;
int chip_id;
int error;
@@ -543,7 +435,7 @@ static int bma150_probe(struct i2c_client *client,
return -EINVAL;
}
- bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+ bma150 = devm_kzalloc(&client->dev, sizeof(*bma150), GFP_KERNEL);
if (!bma150)
return -ENOMEM;
@@ -556,7 +448,7 @@ static int bma150_probe(struct i2c_client *client,
dev_err(&client->dev,
"IRQ GPIO conf. error %d, error %d\n",
client->irq, error);
- goto err_free_mem;
+ return error;
}
}
cfg = &pdata->cfg;
@@ -566,14 +458,42 @@ static int bma150_probe(struct i2c_client *client,
error = bma150_initialize(bma150, cfg);
if (error)
- goto err_free_mem;
+ return error;
- if (client->irq > 0) {
- error = bma150_register_input_device(bma150);
+ idev = devm_input_allocate_device(&bma150->client->dev);
+ if (!idev)
+ return -ENOMEM;
+
+ input_set_drvdata(idev, bma150);
+ bma150->input = idev;
+
+ idev->name = BMA150_DRIVER;
+ idev->phys = BMA150_DRIVER "/input0";
+ idev->id.bustype = BUS_I2C;
+
+ idev->open = bma150_open;
+ idev->close = bma150_close;
+
+ input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+
+ if (client->irq <= 0) {
+ error = input_setup_polling(idev, bma150_poll);
if (error)
- goto err_free_mem;
+ return error;
- error = request_threaded_irq(client->irq,
+ input_set_poll_interval(idev, BMA150_POLL_INTERVAL);
+ input_set_min_poll_interval(idev, BMA150_POLL_MIN);
+ input_set_max_poll_interval(idev, BMA150_POLL_MAX);
+ }
+
+ error = input_register_device(idev);
+ if (error)
+ return error;
+
+ if (client->irq > 0) {
+ error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, bma150_irq_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
BMA150_DRIVER, bma150);
@@ -581,13 +501,8 @@ static int bma150_probe(struct i2c_client *client,
dev_err(&client->dev,
"irq request failed %d, error %d\n",
client->irq, error);
- input_unregister_device(bma150->input);
- goto err_free_mem;
+ return error;
}
- } else {
- error = bma150_register_polled_device(bma150);
- if (error)
- goto err_free_mem;
}
i2c_set_clientdata(client, bma150);
@@ -595,33 +510,14 @@ static int bma150_probe(struct i2c_client *client,
pm_runtime_enable(&client->dev);
return 0;
-
-err_free_mem:
- kfree(bma150);
- return error;
}
-static int bma150_remove(struct i2c_client *client)
+static void bma150_remove(struct i2c_client *client)
{
- struct bma150_data *bma150 = i2c_get_clientdata(client);
-
pm_runtime_disable(&client->dev);
-
- if (client->irq > 0) {
- free_irq(client->irq, bma150);
- input_unregister_device(bma150->input);
- } else {
- input_unregister_polled_device(bma150->input_polled);
- input_free_polled_device(bma150->input_polled);
- }
-
- kfree(bma150);
-
- return 0;
}
-#ifdef CONFIG_PM
-static int bma150_suspend(struct device *dev)
+static int __maybe_unused bma150_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bma150_data *bma150 = i2c_get_clientdata(client);
@@ -629,21 +525,20 @@ static int bma150_suspend(struct device *dev)
return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
}
-static int bma150_resume(struct device *dev)
+static int __maybe_unused bma150_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bma150_data *bma150 = i2c_get_clientdata(client);
return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
}
-#endif
static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
static const struct i2c_device_id bma150_id[] = {
- { "bma150", 0 },
- { "smb380", 0 },
- { "bma023", 0 },
+ { "bma150" },
+ { "smb380" },
+ { "bma023" },
{ }
};
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 23c191a2a071..0cfe5d4a573c 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the VoIP USB phones with CM109 chipsets.
*
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
/*
@@ -358,6 +355,35 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
__func__, error);
}
+static void cm109_submit_ctl(struct cm109_dev *dev)
+{
+ int error;
+
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+
+ dev->irq_urb_pending = 0;
+
+ if (unlikely(dev->shutdown))
+ return;
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
+}
+
/*
* IRQ handler
*/
@@ -365,8 +391,6 @@ static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
- int error;
- unsigned long flags;
dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
@@ -404,32 +428,7 @@ static void cm109_urb_irq_callback(struct urb *urb)
}
out:
-
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
-
- dev->irq_urb_pending = 0;
-
- if (likely(!dev->shutdown)) {
-
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
-
- dev->buzzer_pending = 0;
- dev->ctl_urb_pending = 1;
-
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
- }
-
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+ cm109_submit_ctl(dev);
}
static void cm109_urb_ctl_callback(struct urb *urb)
@@ -437,7 +436,6 @@ static void cm109_urb_ctl_callback(struct urb *urb)
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
- unsigned long flags;
dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
@@ -452,35 +450,31 @@ static void cm109_urb_ctl_callback(struct urb *urb)
__func__, status);
}
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
- if (likely(!dev->shutdown)) {
-
- if (dev->buzzer_pending || status) {
- dev->buzzer_pending = 0;
- dev->ctl_urb_pending = 1;
- cm109_submit_buzz_toggle(dev);
- } else if (likely(!dev->irq_urb_pending)) {
- /* ask for key data */
- dev->irq_urb_pending = 1;
- error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_irq) failed %d\n",
- __func__, error);
- }
- }
+ if (unlikely(dev->shutdown))
+ return;
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+ if (dev->buzzer_pending || status) {
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ } else if (likely(!dev->irq_urb_pending)) {
+ /* ask for key data */
+ dev->irq_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_irq) failed %d\n",
+ __func__, error);
+ }
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
@@ -489,8 +483,6 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
-
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
@@ -559,29 +551,30 @@ static int cm109_input_open(struct input_dev *idev)
return error;
}
- mutex_lock(&dev->pm_mutex);
+ scoped_guard(mutex, &dev->pm_mutex) {
+ dev->buzzer_state = 0;
+ dev->key_code = -1; /* no keys pressed */
+ dev->keybit = 0xf;
- dev->buzzer_state = 0;
- dev->key_code = -1; /* no keys pressed */
- dev->keybit = 0xf;
-
- /* issue INIT */
- dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
- dev->ctl_data->byte[HID_OR3] = 0x00;
+ /* issue INIT */
+ dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ dev->ctl_data->byte[HID_OR3] = 0x00;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (error)
- dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
- else
- dev->open = 1;
+ dev->ctl_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+ if (!error) {
+ dev->open = 1;
+ return 0;
+ }
+ }
- mutex_unlock(&dev->pm_mutex);
+ dev->ctl_urb_pending = 0;
+ usb_autopm_put_interface(dev->intf);
- if (error)
- usb_autopm_put_interface(dev->intf);
+ dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
return error;
}
@@ -590,17 +583,15 @@ static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
- mutex_lock(&dev->pm_mutex);
-
- /*
- * Once we are here event delivery is stopped so we
- * don't need to worry about someone starting buzzer
- * again
- */
- cm109_stop_traffic(dev);
- dev->open = 0;
-
- mutex_unlock(&dev->pm_mutex);
+ scoped_guard(mutex, &dev->pm_mutex) {
+ /*
+ * Once we are here event delivery is stopped so we
+ * don't need to worry about someone starting buzzer
+ * again
+ */
+ cm109_stop_traffic(dev);
+ dev->open = 0;
+ }
usb_autopm_put_interface(dev->intf);
}
@@ -666,12 +657,8 @@ static const struct usb_device_id cm109_usb_table[] = {
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
kfree(dev->ctl_req);
- if (dev->ctl_data)
- usb_free_coherent(dev->udev, USB_PKT_LEN,
- dev->ctl_data, dev->ctl_dma);
- if (dev->irq_data)
- usb_free_coherent(dev->udev, USB_PKT_LEN,
- dev->irq_data, dev->irq_dma);
+ usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
+ usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
@@ -749,7 +736,7 @@ static int cm109_usb_probe(struct usb_interface *intf,
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
- ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ ret = usb_maxpacket(udev, pipe);
if (ret != USB_PKT_LEN)
dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
ret, USB_PKT_LEN);
@@ -827,9 +814,9 @@ static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
- mutex_lock(&dev->pm_mutex);
+ guard(mutex)(&dev->pm_mutex);
+
cm109_stop_traffic(dev);
- mutex_unlock(&dev->pm_mutex);
return 0;
}
@@ -840,9 +827,9 @@ static int cm109_usb_resume(struct usb_interface *intf)
dev_info(&intf->dev, "cm109: usb_resume\n");
- mutex_lock(&dev->pm_mutex);
+ guard(mutex)(&dev->pm_mutex);
+
cm109_restore_state(dev);
- mutex_unlock(&dev->pm_mutex);
return 0;
}
diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c
index c7d00748277b..b4232b0a3957 100644
--- a/drivers/input/misc/cma3000_d0x.c
+++ b/drivers/input/misc/cma3000_d0x.c
@@ -1,22 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* VTI CMA3000_D0x Accelerometer driver
*
* Copyright (C) 2010 Texas Instruments
* Author: Hemanth V <hemanthv@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/export.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -228,15 +218,13 @@ static int cma3000_open(struct input_dev *input_dev)
{
struct cma3000_accl_data *data = input_get_drvdata(input_dev);
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
if (!data->suspended)
cma3000_poweron(data);
data->opened = true;
- mutex_unlock(&data->mutex);
-
return 0;
}
@@ -244,40 +232,34 @@ static void cma3000_close(struct input_dev *input_dev)
{
struct cma3000_accl_data *data = input_get_drvdata(input_dev);
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
if (!data->suspended)
cma3000_poweroff(data);
data->opened = false;
-
- mutex_unlock(&data->mutex);
}
void cma3000_suspend(struct cma3000_accl_data *data)
{
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
if (!data->suspended && data->opened)
cma3000_poweroff(data);
data->suspended = true;
-
- mutex_unlock(&data->mutex);
}
EXPORT_SYMBOL(cma3000_suspend);
void cma3000_resume(struct cma3000_accl_data *data)
{
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
if (data->suspended && data->opened)
cma3000_poweron(data);
data->suspended = false;
-
- mutex_unlock(&data->mutex);
}
EXPORT_SYMBOL(cma3000_resume);
@@ -303,7 +285,7 @@ struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
goto err_out;
}
- data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
error = -ENOMEM;
@@ -336,8 +318,6 @@ struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
input_dev->open = cma3000_open;
input_dev->close = cma3000_close;
- __set_bit(EV_ABS, input_dev->evbit);
-
input_set_abs_params(input_dev, ABS_X,
-data->g_range, data->g_range, pdata->fuzz_x, 0);
input_set_abs_params(input_dev, ABS_Y,
diff --git a/drivers/input/misc/cma3000_d0x.h b/drivers/input/misc/cma3000_d0x.h
index 2304ce306e1c..05ad42a564df 100644
--- a/drivers/input/misc/cma3000_d0x.h
+++ b/drivers/input/misc/cma3000_d0x.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* VTI CMA3000_D0x Accelerometer driver
*
* Copyright (C) 2010 Texas Instruments
* Author: Hemanth V <hemanthv@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _INPUT_CMA3000_H
diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c
index c7021916b64b..f892c5b1e4bd 100644
--- a/drivers/input/misc/cma3000_d0x_i2c.c
+++ b/drivers/input/misc/cma3000_d0x_i2c.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implements I2C interface for VTI CMA300_D0x Accelerometer driver
*
* Copyright (C) 2010 Texas Instruments
* Author: Hemanth V <hemanthv@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
@@ -55,8 +44,7 @@ static const struct cma3000_bus_ops cma3000_i2c_bops = {
.write = cma3000_i2c_set,
};
-static int cma3000_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cma3000_i2c_probe(struct i2c_client *client)
{
struct cma3000_accl_data *data;
@@ -69,16 +57,13 @@ static int cma3000_i2c_probe(struct i2c_client *client,
return 0;
}
-static int cma3000_i2c_remove(struct i2c_client *client)
+static void cma3000_i2c_remove(struct i2c_client *client)
{
struct cma3000_accl_data *data = i2c_get_clientdata(client);
cma3000_exit(data);
-
- return 0;
}
-#ifdef CONFIG_PM
static int cma3000_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -103,11 +88,10 @@ static const struct dev_pm_ops cma3000_i2c_pm_ops = {
.suspend = cma3000_i2c_suspend,
.resume = cma3000_i2c_resume,
};
-#endif
static const struct i2c_device_id cma3000_i2c_id[] = {
- { "cma3000_d01", 0 },
- { },
+ { "cma3000_d01" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id);
@@ -118,9 +102,7 @@ static struct i2c_driver cma3000_i2c_driver = {
.id_table = cma3000_i2c_id,
.driver = {
.name = "cma3000_i2c_accl",
-#ifdef CONFIG_PM
- .pm = &cma3000_i2c_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&cma3000_i2c_pm_ops),
},
};
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
index fbe72afc9347..b1624f5414ee 100644
--- a/drivers/input/misc/cobalt_btns.c
+++ b/drivers/input/misc/cobalt_btns.c
@@ -1,23 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Cobalt button interface driver.
*
* Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/input-polldev.h>
+#include <linux/input.h>
+#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -39,16 +27,14 @@ static const unsigned short cobalt_map[] = {
};
struct buttons_dev {
- struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(cobalt_map)];
int count[ARRAY_SIZE(cobalt_map)];
void __iomem *reg;
};
-static void handle_buttons(struct input_polled_dev *dev)
+static void handle_buttons(struct input_dev *input)
{
- struct buttons_dev *bdev = dev->private;
- struct input_dev *input = dev->input;
+ struct buttons_dev *bdev = input_get_drvdata(input);
uint32_t status;
int i;
@@ -75,29 +61,33 @@ static void handle_buttons(struct input_polled_dev *dev)
static int cobalt_buttons_probe(struct platform_device *pdev)
{
struct buttons_dev *bdev;
- struct input_polled_dev *poll_dev;
struct input_dev *input;
struct resource *res;
int error, i;
- bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
- poll_dev = input_allocate_polled_device();
- if (!bdev || !poll_dev) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EBUSY;
+
+ bdev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!bdev->reg)
+ return -ENOMEM;
memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
- poll_dev->private = bdev;
- poll_dev->poll = handle_buttons;
- poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return -ENOMEM;
+
+ input_set_drvdata(input, bdev);
- input = poll_dev->input;
input->name = "Cobalt buttons";
input->phys = "cobalt/input0";
input->id.bustype = BUS_HOST;
- input->dev.parent = &pdev->dev;
input->keycode = bdev->keymap;
input->keycodemax = ARRAY_SIZE(bdev->keymap);
@@ -109,39 +99,16 @@ static int cobalt_buttons_probe(struct platform_device *pdev)
__set_bit(bdev->keymap[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- error = -EBUSY;
- goto err_free_mem;
- }
-
- bdev->poll_dev = poll_dev;
- bdev->reg = ioremap(res->start, resource_size(res));
- dev_set_drvdata(&pdev->dev, bdev);
- error = input_register_polled_device(poll_dev);
+ error = input_setup_polling(input, handle_buttons);
if (error)
- goto err_iounmap;
-
- return 0;
+ return error;
- err_iounmap:
- iounmap(bdev->reg);
- err_free_mem:
- input_free_polled_device(poll_dev);
- kfree(bdev);
- return error;
-}
-
-static int cobalt_buttons_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct buttons_dev *bdev = dev_get_drvdata(dev);
+ input_set_poll_interval(input, BUTTONS_POLL_INTERVAL);
- input_unregister_polled_device(bdev->poll_dev);
- input_free_polled_device(bdev->poll_dev);
- iounmap(bdev->reg);
- kfree(bdev);
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
@@ -154,7 +121,6 @@ MODULE_ALIAS("platform:Cobalt buttons");
static struct platform_driver cobalt_buttons_driver = {
.probe = cobalt_buttons_probe,
- .remove = cobalt_buttons_remove,
.driver = {
.name = "Cobalt buttons",
},
diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c
index 0abef63217e2..85cddb84717a 100644
--- a/drivers/input/misc/cpcap-pwrbutton.c
+++ b/drivers/input/misc/cpcap-pwrbutton.c
@@ -1,16 +1,8 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* CPCAP Power Button Input Driver
*
* Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file "COPYING" in the main directory of this
- * archive for more details.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -54,9 +46,13 @@ static irqreturn_t powerbutton_irq(int irq, void *_button)
static int cpcap_power_button_probe(struct platform_device *pdev)
{
struct cpcap_power_button *button;
- int irq = platform_get_irq(pdev, 0);
+ int irq;
int err;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
if (!button)
return -ENOMEM;
@@ -73,7 +69,6 @@ static int cpcap_power_button_probe(struct platform_device *pdev)
button->idev->name = "cpcap-pwrbutton";
button->idev->phys = "cpcap-pwrbutton/input0";
- button->idev->dev.parent = button->dev;
input_set_capability(button->idev, EV_KEY, KEY_POWER);
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c
new file mode 100644
index 000000000000..7aa7d577e01b
--- /dev/null
+++ b/drivers/input/misc/cs40l50-vibra.c
@@ -0,0 +1,558 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with waveform memory,
+ * integrated DSP, and closed-loop algorithms
+ *
+ * Copyright 2024 Cirrus Logic, Inc.
+ *
+ * Author: James Ogletree <james.ogletree@cirrus.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/input.h>
+#include <linux/mfd/cs40l50.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+/* Wavetables */
+#define CS40L50_RAM_INDEX_START 0x1000000
+#define CS40L50_RAM_INDEX_END 0x100007F
+#define CS40L50_RTH_INDEX_START 0x1400000
+#define CS40L50_RTH_INDEX_END 0x1400001
+#define CS40L50_ROM_INDEX_START 0x1800000
+#define CS40L50_ROM_INDEX_END 0x180001A
+#define CS40L50_TYPE_PCM 8
+#define CS40L50_TYPE_PWLE 12
+#define CS40L50_PCM_ID 0x0
+#define CS40L50_OWT_CUSTOM_DATA_SIZE 2
+#define CS40L50_CUSTOM_DATA_MASK 0xFFFFU
+
+/* DSP */
+#define CS40L50_GPIO_BASE 0x2804140
+#define CS40L50_OWT_BASE 0x2805C34
+#define CS40L50_OWT_SIZE 0x2805C38
+#define CS40L50_OWT_NEXT 0x2805C3C
+#define CS40L50_EFFECTS_MAX 1
+
+/* GPIO */
+#define CS40L50_GPIO_NUM_MASK GENMASK(14, 12)
+#define CS40L50_GPIO_EDGE_MASK BIT(15)
+#define CS40L50_GPIO_MAPPING_NONE 0
+#define CS40L50_GPIO_DISABLE 0x1FF
+
+enum cs40l50_bank_type {
+ CS40L50_WVFRM_BANK_RAM,
+ CS40L50_WVFRM_BANK_ROM,
+ CS40L50_WVFRM_BANK_OWT,
+ CS40L50_WVFRM_BANK_NUM,
+};
+
+/* Describes an area in DSP memory populated by effects */
+struct cs40l50_bank {
+ enum cs40l50_bank_type type;
+ u32 base_index;
+ u32 max_index;
+};
+
+struct cs40l50_effect {
+ enum cs40l50_bank_type type;
+ struct list_head list;
+ u32 gpio_reg;
+ u32 index;
+ int id;
+};
+
+/* Describes haptic interface of loaded DSP firmware */
+struct cs40l50_vibra_dsp {
+ struct cs40l50_bank *banks;
+ u32 gpio_base_reg;
+ u32 owt_offset_reg;
+ u32 owt_size_reg;
+ u32 owt_base_reg;
+ u32 push_owt_cmd;
+ u32 delete_owt_cmd;
+ u32 stop_cmd;
+ int (*write)(struct device *dev, struct regmap *regmap, u32 val);
+};
+
+/* Describes configuration and state of haptic operations */
+struct cs40l50_vibra {
+ struct device *dev;
+ struct regmap *regmap;
+ struct input_dev *input;
+ struct workqueue_struct *vib_wq;
+ struct list_head effect_head;
+ struct cs40l50_vibra_dsp dsp;
+};
+
+struct cs40l50_work {
+ struct cs40l50_vibra *vib;
+ struct ff_effect *effect;
+ struct work_struct work;
+ s16 *custom_data;
+ int custom_len;
+ int count;
+ int error;
+};
+
+static struct cs40l50_bank cs40l50_banks[] = {
+ {
+ .type = CS40L50_WVFRM_BANK_RAM,
+ .base_index = CS40L50_RAM_INDEX_START,
+ .max_index = CS40L50_RAM_INDEX_END,
+ },
+ {
+ .type = CS40L50_WVFRM_BANK_ROM,
+ .base_index = CS40L50_ROM_INDEX_START,
+ .max_index = CS40L50_ROM_INDEX_END,
+ },
+ {
+ .type = CS40L50_WVFRM_BANK_OWT,
+ .base_index = CS40L50_RTH_INDEX_START,
+ .max_index = CS40L50_RTH_INDEX_END,
+ },
+};
+
+static struct cs40l50_vibra_dsp cs40l50_dsp = {
+ .banks = cs40l50_banks,
+ .gpio_base_reg = CS40L50_GPIO_BASE,
+ .owt_base_reg = CS40L50_OWT_BASE,
+ .owt_offset_reg = CS40L50_OWT_NEXT,
+ .owt_size_reg = CS40L50_OWT_SIZE,
+ .push_owt_cmd = CS40L50_OWT_PUSH,
+ .delete_owt_cmd = CS40L50_OWT_DELETE,
+ .stop_cmd = CS40L50_STOP_PLAYBACK,
+ .write = cs40l50_dsp_write,
+};
+
+static struct cs40l50_effect *cs40l50_find_effect(int id, struct list_head *effect_head)
+{
+ struct cs40l50_effect *effect;
+
+ list_for_each_entry(effect, effect_head, list)
+ if (effect->id == id)
+ return effect;
+
+ return NULL;
+}
+
+static int cs40l50_effect_bank_set(struct cs40l50_work *work_data,
+ struct cs40l50_effect *effect)
+{
+ s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK;
+
+ if (bank_type >= CS40L50_WVFRM_BANK_NUM) {
+ dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type);
+ return -EINVAL;
+ }
+
+ if (work_data->custom_len > CS40L50_OWT_CUSTOM_DATA_SIZE)
+ effect->type = CS40L50_WVFRM_BANK_OWT;
+ else
+ effect->type = bank_type;
+
+ return 0;
+}
+
+static int cs40l50_effect_index_set(struct cs40l50_work *work_data,
+ struct cs40l50_effect *effect)
+{
+ struct cs40l50_vibra *vib = work_data->vib;
+ struct cs40l50_effect *owt_effect;
+ u32 base_index, max_index;
+
+ base_index = vib->dsp.banks[effect->type].base_index;
+ max_index = vib->dsp.banks[effect->type].max_index;
+
+ effect->index = base_index;
+
+ switch (effect->type) {
+ case CS40L50_WVFRM_BANK_OWT:
+ list_for_each_entry(owt_effect, &vib->effect_head, list)
+ if (owt_effect->type == CS40L50_WVFRM_BANK_OWT)
+ effect->index++;
+ break;
+ case CS40L50_WVFRM_BANK_ROM:
+ case CS40L50_WVFRM_BANK_RAM:
+ effect->index += work_data->custom_data[1] & CS40L50_CUSTOM_DATA_MASK;
+ break;
+ default:
+ dev_err(vib->dev, "Bank type %d not supported\n", effect->type);
+ return -EINVAL;
+ }
+
+ if (effect->index > max_index || effect->index < base_index) {
+ dev_err(vib->dev, "Index out of bounds: %u\n", effect->index);
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+static int cs40l50_effect_gpio_mapping_set(struct cs40l50_work *work_data,
+ struct cs40l50_effect *effect)
+{
+ u16 gpio_edge, gpio_num, button = work_data->effect->trigger.button;
+ struct cs40l50_vibra *vib = work_data->vib;
+
+ if (button) {
+ gpio_num = FIELD_GET(CS40L50_GPIO_NUM_MASK, button);
+ gpio_edge = FIELD_GET(CS40L50_GPIO_EDGE_MASK, button);
+ effect->gpio_reg = vib->dsp.gpio_base_reg + (gpio_num * 8) - gpio_edge;
+
+ return regmap_write(vib->regmap, effect->gpio_reg, button);
+ }
+
+ effect->gpio_reg = CS40L50_GPIO_MAPPING_NONE;
+
+ return 0;
+}
+
+struct cs40l50_owt_header {
+ u32 type;
+ u32 data_words;
+ u32 offset;
+} __packed;
+
+static int cs40l50_upload_owt(struct cs40l50_work *work_data)
+{
+ u8 *new_owt_effect_data __free(kfree) = NULL;
+ struct cs40l50_vibra *vib = work_data->vib;
+ size_t len = work_data->custom_len * 2;
+ struct cs40l50_owt_header header;
+ u32 offset, size;
+ int error;
+
+ error = regmap_read(vib->regmap, vib->dsp.owt_size_reg, &size);
+ if (error)
+ return error;
+
+ if ((size * sizeof(u32)) < sizeof(header) + len) {
+ dev_err(vib->dev, "No space in open wavetable for effect\n");
+ return -ENOSPC;
+ }
+
+ header.type = work_data->custom_data[0] == CS40L50_PCM_ID ? CS40L50_TYPE_PCM :
+ CS40L50_TYPE_PWLE;
+ header.offset = sizeof(header) / sizeof(u32);
+ header.data_words = len / sizeof(u32);
+
+ new_owt_effect_data = kmalloc(sizeof(header) + len, GFP_KERNEL);
+ if (!new_owt_effect_data)
+ return -ENOMEM;
+
+ memcpy(new_owt_effect_data, &header, sizeof(header));
+ memcpy(new_owt_effect_data + sizeof(header), work_data->custom_data, len);
+
+ error = regmap_read(vib->regmap, vib->dsp.owt_offset_reg, &offset);
+ if (error)
+ return error;
+
+ error = regmap_bulk_write(vib->regmap, vib->dsp.owt_base_reg +
+ (offset * sizeof(u32)), new_owt_effect_data,
+ sizeof(header) + len);
+ if (error)
+ return error;
+
+ error = vib->dsp.write(vib->dev, vib->regmap, vib->dsp.push_owt_cmd);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void cs40l50_add_worker(struct work_struct *work)
+{
+ struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
+ struct cs40l50_vibra *vib = work_data->vib;
+ struct cs40l50_effect *effect;
+ bool is_new = false;
+ int error;
+
+ error = pm_runtime_resume_and_get(vib->dev);
+ if (error)
+ goto err_exit;
+
+ /* Update effect if already uploaded, otherwise create new effect */
+ effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
+ if (!effect) {
+ effect = kzalloc(sizeof(*effect), GFP_KERNEL);
+ if (!effect) {
+ error = -ENOMEM;
+ goto err_pm;
+ }
+
+ effect->id = work_data->effect->id;
+ is_new = true;
+ }
+
+ error = cs40l50_effect_bank_set(work_data, effect);
+ if (error)
+ goto err_free;
+
+ error = cs40l50_effect_index_set(work_data, effect);
+ if (error)
+ goto err_free;
+
+ error = cs40l50_effect_gpio_mapping_set(work_data, effect);
+ if (error)
+ goto err_free;
+
+ if (effect->type == CS40L50_WVFRM_BANK_OWT)
+ error = cs40l50_upload_owt(work_data);
+err_free:
+ if (is_new) {
+ if (error)
+ kfree(effect);
+ else
+ list_add(&effect->list, &vib->effect_head);
+ }
+err_pm:
+ pm_runtime_mark_last_busy(vib->dev);
+ pm_runtime_put_autosuspend(vib->dev);
+err_exit:
+ work_data->error = error;
+}
+
+static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct ff_periodic_effect *periodic = &effect->u.periodic;
+ struct cs40l50_vibra *vib = input_get_drvdata(dev);
+ struct cs40l50_work work_data;
+
+ if (effect->type != FF_PERIODIC || periodic->waveform != FF_CUSTOM) {
+ dev_err(vib->dev, "Type (%#X) or waveform (%#X) unsupported\n",
+ effect->type, periodic->waveform);
+ return -EINVAL;
+ }
+
+ work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data,
+ effect->u.periodic.custom_len,
+ sizeof(s16));
+ if (IS_ERR(work_data.custom_data))
+ return PTR_ERR(work_data.custom_data);
+
+ work_data.custom_len = effect->u.periodic.custom_len;
+ work_data.vib = vib;
+ work_data.effect = effect;
+ INIT_WORK_ONSTACK(&work_data.work, cs40l50_add_worker);
+
+ /* Push to the workqueue to serialize with playbacks */
+ queue_work(vib->vib_wq, &work_data.work);
+ flush_work(&work_data.work);
+ destroy_work_on_stack(&work_data.work);
+
+ kfree(work_data.custom_data);
+
+ return work_data.error;
+}
+
+static void cs40l50_start_worker(struct work_struct *work)
+{
+ struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
+ struct cs40l50_vibra *vib = work_data->vib;
+ struct cs40l50_effect *start_effect;
+
+ if (pm_runtime_resume_and_get(vib->dev) < 0)
+ goto err_free;
+
+ start_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
+ if (start_effect) {
+ while (--work_data->count >= 0) {
+ vib->dsp.write(vib->dev, vib->regmap, start_effect->index);
+ usleep_range(work_data->effect->replay.length,
+ work_data->effect->replay.length + 100);
+ }
+ } else {
+ dev_err(vib->dev, "Effect to play not found\n");
+ }
+
+ pm_runtime_mark_last_busy(vib->dev);
+ pm_runtime_put_autosuspend(vib->dev);
+err_free:
+ kfree(work_data);
+}
+
+static void cs40l50_stop_worker(struct work_struct *work)
+{
+ struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
+ struct cs40l50_vibra *vib = work_data->vib;
+
+ if (pm_runtime_resume_and_get(vib->dev) < 0)
+ return;
+
+ vib->dsp.write(vib->dev, vib->regmap, vib->dsp.stop_cmd);
+
+ pm_runtime_mark_last_busy(vib->dev);
+ pm_runtime_put_autosuspend(vib->dev);
+
+ kfree(work_data);
+}
+
+static int cs40l50_playback(struct input_dev *dev, int effect_id, int val)
+{
+ struct cs40l50_vibra *vib = input_get_drvdata(dev);
+ struct cs40l50_work *work_data;
+
+ work_data = kzalloc(sizeof(*work_data), GFP_ATOMIC);
+ if (!work_data)
+ return -ENOMEM;
+
+ work_data->vib = vib;
+
+ if (val > 0) {
+ work_data->effect = &dev->ff->effects[effect_id];
+ work_data->count = val;
+ INIT_WORK(&work_data->work, cs40l50_start_worker);
+ } else {
+ /* Stop the amplifier as device drives only one effect */
+ INIT_WORK(&work_data->work, cs40l50_stop_worker);
+ }
+
+ queue_work(vib->vib_wq, &work_data->work);
+
+ return 0;
+}
+
+static void cs40l50_erase_worker(struct work_struct *work)
+{
+ struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
+ struct cs40l50_effect *erase_effect, *owt_effect;
+ struct cs40l50_vibra *vib = work_data->vib;
+ int error;
+
+ error = pm_runtime_resume_and_get(vib->dev);
+ if (error)
+ goto err_exit;
+
+ erase_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
+ if (!erase_effect) {
+ dev_err(vib->dev, "Effect to erase not found\n");
+ error = -EINVAL;
+ goto err_pm;
+ }
+
+ if (erase_effect->gpio_reg != CS40L50_GPIO_MAPPING_NONE) {
+ error = regmap_write(vib->regmap, erase_effect->gpio_reg,
+ CS40L50_GPIO_DISABLE);
+ if (error)
+ goto err_pm;
+ }
+
+ if (erase_effect->type == CS40L50_WVFRM_BANK_OWT) {
+ error = vib->dsp.write(vib->dev, vib->regmap,
+ vib->dsp.delete_owt_cmd |
+ (erase_effect->index & 0xFF));
+ if (error)
+ goto err_pm;
+
+ list_for_each_entry(owt_effect, &vib->effect_head, list)
+ if (owt_effect->type == CS40L50_WVFRM_BANK_OWT &&
+ owt_effect->index > erase_effect->index)
+ owt_effect->index--;
+ }
+
+ list_del(&erase_effect->list);
+ kfree(erase_effect);
+err_pm:
+ pm_runtime_mark_last_busy(vib->dev);
+ pm_runtime_put_autosuspend(vib->dev);
+err_exit:
+ work_data->error = error;
+}
+
+static int cs40l50_erase(struct input_dev *dev, int effect_id)
+{
+ struct cs40l50_vibra *vib = input_get_drvdata(dev);
+ struct cs40l50_work work_data;
+
+ work_data.vib = vib;
+ work_data.effect = &dev->ff->effects[effect_id];
+
+ INIT_WORK_ONSTACK(&work_data.work, cs40l50_erase_worker);
+
+ /* Push to workqueue to serialize with playbacks */
+ queue_work(vib->vib_wq, &work_data.work);
+ flush_work(&work_data.work);
+ destroy_work_on_stack(&work_data.work);
+
+ return work_data.error;
+}
+
+static void cs40l50_remove_wq(void *data)
+{
+ destroy_workqueue(data);
+}
+
+static int cs40l50_vibra_probe(struct platform_device *pdev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
+ struct cs40l50_vibra *vib;
+ int error;
+
+ vib = devm_kzalloc(pdev->dev.parent, sizeof(*vib), GFP_KERNEL);
+ if (!vib)
+ return -ENOMEM;
+
+ vib->dev = cs40l50->dev;
+ vib->regmap = cs40l50->regmap;
+ vib->dsp = cs40l50_dsp;
+
+ vib->input = devm_input_allocate_device(vib->dev);
+ if (!vib->input)
+ return -ENOMEM;
+
+ vib->input->id.product = cs40l50->devid;
+ vib->input->id.version = cs40l50->revid;
+ vib->input->name = "cs40l50_vibra";
+
+ input_set_drvdata(vib->input, vib);
+ input_set_capability(vib->input, EV_FF, FF_PERIODIC);
+ input_set_capability(vib->input, EV_FF, FF_CUSTOM);
+
+ error = input_ff_create(vib->input, CS40L50_EFFECTS_MAX);
+ if (error) {
+ dev_err(vib->dev, "Failed to create input device\n");
+ return error;
+ }
+
+ vib->input->ff->upload = cs40l50_add;
+ vib->input->ff->playback = cs40l50_playback;
+ vib->input->ff->erase = cs40l50_erase;
+
+ INIT_LIST_HEAD(&vib->effect_head);
+
+ vib->vib_wq = alloc_ordered_workqueue("vib_wq", WQ_HIGHPRI);
+ if (!vib->vib_wq)
+ return -ENOMEM;
+
+ error = devm_add_action_or_reset(vib->dev, cs40l50_remove_wq, vib->vib_wq);
+ if (error)
+ return error;
+
+ error = input_register_device(vib->input);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static const struct platform_device_id cs40l50_vibra_id_match[] = {
+ { "cs40l50-vibra", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs40l50_vibra_id_match);
+
+static struct platform_driver cs40l50_vibra_driver = {
+ .probe = cs40l50_vibra_probe,
+ .id_table = cs40l50_vibra_id_match,
+ .driver = {
+ .name = "cs40l50-vibra",
+ },
+};
+module_platform_driver(cs40l50_vibra_driver);
+
+MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c
new file mode 100644
index 000000000000..e4a605c6af15
--- /dev/null
+++ b/drivers/input/misc/da7280.c
@@ -0,0 +1,1328 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DA7280 Haptic device driver
+ *
+ * Copyright (c) 2020 Dialog Semiconductor.
+ * Author: Roy Im <Roy.Im.Opensource@diasemi.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+
+/* Registers */
+#define DA7280_IRQ_EVENT1 0x03
+#define DA7280_IRQ_EVENT_WARNING_DIAG 0x04
+#define DA7280_IRQ_EVENT_SEQ_DIAG 0x05
+#define DA7280_IRQ_STATUS1 0x06
+#define DA7280_IRQ_MASK1 0x07
+#define DA7280_FRQ_LRA_PER_H 0x0A
+#define DA7280_FRQ_LRA_PER_L 0x0B
+#define DA7280_ACTUATOR1 0x0C
+#define DA7280_ACTUATOR2 0x0D
+#define DA7280_ACTUATOR3 0x0E
+#define DA7280_CALIB_V2I_H 0x0F
+#define DA7280_CALIB_V2I_L 0x10
+#define DA7280_TOP_CFG1 0x13
+#define DA7280_TOP_CFG2 0x14
+#define DA7280_TOP_CFG4 0x16
+#define DA7280_TOP_INT_CFG1 0x17
+#define DA7280_TOP_CTL1 0x22
+#define DA7280_TOP_CTL2 0x23
+#define DA7280_SEQ_CTL2 0x28
+#define DA7280_GPI_0_CTL 0x29
+#define DA7280_GPI_1_CTL 0x2A
+#define DA7280_GPI_2_CTL 0x2B
+#define DA7280_MEM_CTL1 0x2C
+#define DA7280_MEM_CTL2 0x2D
+#define DA7280_TOP_CFG5 0x6E
+#define DA7280_IRQ_MASK2 0x83
+#define DA7280_SNP_MEM_99 0xE7
+
+/* Register field */
+
+/* DA7280_IRQ_EVENT1 (Address 0x03) */
+#define DA7280_E_SEQ_CONTINUE_MASK BIT(0)
+#define DA7280_E_UVLO_MASK BIT(1)
+#define DA7280_E_SEQ_DONE_MASK BIT(2)
+#define DA7280_E_OVERTEMP_CRIT_MASK BIT(3)
+#define DA7280_E_SEQ_FAULT_MASK BIT(4)
+#define DA7280_E_WARNING_MASK BIT(5)
+#define DA7280_E_ACTUATOR_FAULT_MASK BIT(6)
+#define DA7280_E_OC_FAULT_MASK BIT(7)
+
+/* DA7280_IRQ_EVENT_WARNING_DIAG (Address 0x04) */
+#define DA7280_E_OVERTEMP_WARN_MASK BIT(3)
+#define DA7280_E_MEM_TYPE_MASK BIT(4)
+#define DA7280_E_LIM_DRIVE_ACC_MASK BIT(6)
+#define DA7280_E_LIM_DRIVE_MASK BIT(7)
+
+/* DA7280_IRQ_EVENT_PAT_DIAG (Address 0x05) */
+#define DA7280_E_PWM_FAULT_MASK BIT(5)
+#define DA7280_E_MEM_FAULT_MASK BIT(6)
+#define DA7280_E_SEQ_ID_FAULT_MASK BIT(7)
+
+/* DA7280_IRQ_STATUS1 (Address 0x06) */
+#define DA7280_STA_SEQ_CONTINUE_MASK BIT(0)
+#define DA7280_STA_UVLO_VBAT_OK_MASK BIT(1)
+#define DA7280_STA_SEQ_DONE_MASK BIT(2)
+#define DA7280_STA_OVERTEMP_CRIT_MASK BIT(3)
+#define DA7280_STA_SEQ_FAULT_MASK BIT(4)
+#define DA7280_STA_WARNING_MASK BIT(5)
+#define DA7280_STA_ACTUATOR_MASK BIT(6)
+#define DA7280_STA_OC_MASK BIT(7)
+
+/* DA7280_IRQ_MASK1 (Address 0x07) */
+#define DA7280_SEQ_CONTINUE_M_MASK BIT(0)
+#define DA7280_E_UVLO_M_MASK BIT(1)
+#define DA7280_SEQ_DONE_M_MASK BIT(2)
+#define DA7280_OVERTEMP_CRIT_M_MASK BIT(3)
+#define DA7280_SEQ_FAULT_M_MASK BIT(4)
+#define DA7280_WARNING_M_MASK BIT(5)
+#define DA7280_ACTUATOR_M_MASK BIT(6)
+#define DA7280_OC_M_MASK BIT(7)
+
+/* DA7280_ACTUATOR3 (Address 0x0e) */
+#define DA7280_IMAX_MASK GENMASK(4, 0)
+
+/* DA7280_TOP_CFG1 (Address 0x13) */
+#define DA7280_AMP_PID_EN_MASK BIT(0)
+#define DA7280_RAPID_STOP_EN_MASK BIT(1)
+#define DA7280_ACCELERATION_EN_MASK BIT(2)
+#define DA7280_FREQ_TRACK_EN_MASK BIT(3)
+#define DA7280_BEMF_SENSE_EN_MASK BIT(4)
+#define DA7280_ACTUATOR_TYPE_MASK BIT(5)
+
+/* DA7280_TOP_CFG2 (Address 0x14) */
+#define DA7280_FULL_BRAKE_THR_MASK GENMASK(3, 0)
+#define DA7280_MEM_DATA_SIGNED_MASK BIT(4)
+
+/* DA7280_TOP_CFG4 (Address 0x16) */
+#define DA7280_TST_CALIB_IMPEDANCE_DIS_MASK BIT(6)
+#define DA7280_V2I_FACTOR_FREEZE_MASK BIT(7)
+
+/* DA7280_TOP_INT_CFG1 (Address 0x17) */
+#define DA7280_BEMF_FAULT_LIM_MASK GENMASK(1, 0)
+
+/* DA7280_TOP_CTL1 (Address 0x22) */
+#define DA7280_OPERATION_MODE_MASK GENMASK(2, 0)
+#define DA7280_STANDBY_EN_MASK BIT(3)
+#define DA7280_SEQ_START_MASK BIT(4)
+
+/* DA7280_SEQ_CTL2 (Address 0x28) */
+#define DA7280_PS_SEQ_ID_MASK GENMASK(3, 0)
+#define DA7280_PS_SEQ_LOOP_MASK GENMASK(7, 4)
+
+/* DA7280_GPIO_0_CTL (Address 0x29) */
+#define DA7280_GPI0_POLARITY_MASK GENMASK(1, 0)
+#define DA7280_GPI0_MODE_MASK BIT(2)
+#define DA7280_GPI0_SEQUENCE_ID_MASK GENMASK(6, 3)
+
+/* DA7280_GPIO_1_CTL (Address 0x2a) */
+#define DA7280_GPI1_POLARITY_MASK GENMASK(1, 0)
+#define DA7280_GPI1_MODE_MASK BIT(2)
+#define DA7280_GPI1_SEQUENCE_ID_MASK GENMASK(6, 3)
+
+/* DA7280_GPIO_2_CTL (Address 0x2b) */
+#define DA7280_GPI2_POLARITY_MASK GENMASK(1, 0)
+#define DA7280_GPI2_MODE_MASK BIT(2)
+#define DA7280_GPI2_SEQUENCE_ID_MASK GENMASK(6, 3)
+
+/* DA7280_MEM_CTL2 (Address 0x2d) */
+#define DA7280_WAV_MEM_LOCK_MASK BIT(7)
+
+/* DA7280_TOP_CFG5 (Address 0x6e) */
+#define DA7280_V2I_FACTOR_OFFSET_EN_MASK BIT(0)
+
+/* DA7280_IRQ_MASK2 (Address 0x83) */
+#define DA7280_ADC_SAT_M_MASK BIT(7)
+
+/* Controls */
+
+#define DA7280_VOLTAGE_RATE_MAX 6000000
+#define DA7280_VOLTAGE_RATE_STEP 23400
+#define DA7280_NOMMAX_DFT 0x6B
+#define DA7280_ABSMAX_DFT 0x78
+
+#define DA7280_IMPD_MAX 1500000000
+#define DA7280_IMPD_DEFAULT 22000000
+
+#define DA7280_IMAX_DEFAULT 0x0E
+#define DA7280_IMAX_STEP 7200
+#define DA7280_IMAX_LIMIT 252000
+
+#define DA7280_RESONT_FREQH_DFT 0x39
+#define DA7280_RESONT_FREQL_DFT 0x32
+#define DA7280_MIN_RESONAT_FREQ_HZ 50
+#define DA7280_MAX_RESONAT_FREQ_HZ 300
+
+#define DA7280_SEQ_ID_MAX 15
+#define DA7280_SEQ_LOOP_MAX 15
+#define DA7280_GPI_SEQ_ID_DFT 0
+#define DA7280_GPI_SEQ_ID_MAX 2
+
+#define DA7280_SNP_MEM_SIZE 100
+#define DA7280_SNP_MEM_MAX DA7280_SNP_MEM_99
+
+#define DA7280_IRQ_NUM 3
+
+#define DA7280_SKIP_INIT 0x100
+
+#define DA7280_FF_EFFECT_COUNT_MAX 15
+
+/* Maximum gain is 0x7fff for PWM mode */
+#define DA7280_MAX_MAGNITUDE_SHIFT 15
+
+enum da7280_haptic_dev_t {
+ DA7280_LRA = 0,
+ DA7280_ERM_BAR = 1,
+ DA7280_ERM_COIN = 2,
+ DA7280_DEV_MAX,
+};
+
+enum da7280_op_mode {
+ DA7280_INACTIVE = 0,
+ DA7280_DRO_MODE = 1,
+ DA7280_PWM_MODE = 2,
+ DA7280_RTWM_MODE = 3,
+ DA7280_ETWM_MODE = 4,
+ DA7280_OPMODE_MAX,
+};
+
+#define DA7280_FF_CONSTANT_DRO 1
+#define DA7280_FF_PERIODIC_PWM 2
+#define DA7280_FF_PERIODIC_RTWM 1
+#define DA7280_FF_PERIODIC_ETWM 2
+
+#define DA7280_FF_PERIODIC_MODE DA7280_RTWM_MODE
+#define DA7280_FF_CONSTANT_MODE DA7280_DRO_MODE
+
+enum da7280_custom_effect_param {
+ DA7280_CUSTOM_SEQ_ID_IDX = 0,
+ DA7280_CUSTOM_SEQ_LOOP_IDX = 1,
+ DA7280_CUSTOM_DATA_LEN = 2,
+};
+
+enum da7280_custom_gpi_effect_param {
+ DA7280_CUSTOM_GPI_SEQ_ID_IDX = 0,
+ DA7280_CUSTOM_GPI_NUM_IDX = 2,
+ DA7280_CUSTOM_GP_DATA_LEN = 3,
+};
+
+struct da7280_gpi_ctl {
+ u8 seq_id;
+ u8 mode;
+ u8 polarity;
+};
+
+struct da7280_haptic {
+ struct regmap *regmap;
+ struct input_dev *input_dev;
+ struct device *dev;
+ struct i2c_client *client;
+ struct pwm_device *pwm_dev;
+
+ struct work_struct work;
+ int val;
+ u16 gain;
+ s16 level;
+
+ u8 dev_type;
+ u8 op_mode;
+ u8 const_op_mode;
+ u8 periodic_op_mode;
+ u16 nommax;
+ u16 absmax;
+ u32 imax;
+ u32 impd;
+ u32 resonant_freq_h;
+ u32 resonant_freq_l;
+ bool bemf_sense_en;
+ bool freq_track_en;
+ bool acc_en;
+ bool rapid_stop_en;
+ bool amp_pid_en;
+ u8 ps_seq_id;
+ u8 ps_seq_loop;
+ struct da7280_gpi_ctl gpi_ctl[3];
+ bool mem_update;
+ u8 snp_mem[DA7280_SNP_MEM_SIZE];
+ bool active;
+ bool suspended;
+};
+
+static bool da7280_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7280_IRQ_EVENT1:
+ case DA7280_IRQ_EVENT_WARNING_DIAG:
+ case DA7280_IRQ_EVENT_SEQ_DIAG:
+ case DA7280_IRQ_STATUS1:
+ case DA7280_TOP_CTL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da7280_haptic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = DA7280_SNP_MEM_MAX,
+ .volatile_reg = da7280_volatile_register,
+};
+
+static int da7280_haptic_mem_update(struct da7280_haptic *haptics)
+{
+ unsigned int val;
+ int error;
+
+ /* The patterns should be updated when haptic is not working */
+ error = regmap_read(haptics->regmap, DA7280_IRQ_STATUS1, &val);
+ if (error)
+ return error;
+ if (val & DA7280_STA_WARNING_MASK) {
+ dev_warn(haptics->dev,
+ "Warning! Please check HAPTIC status.\n");
+ return -EBUSY;
+ }
+
+ /* Patterns are not updated if the lock bit is enabled */
+ val = 0;
+ error = regmap_read(haptics->regmap, DA7280_MEM_CTL2, &val);
+ if (error)
+ return error;
+ if (~val & DA7280_WAV_MEM_LOCK_MASK) {
+ dev_warn(haptics->dev, "Please unlock the bit first\n");
+ return -EACCES;
+ }
+
+ /* Set to Inactive mode to make sure safety */
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_OPERATION_MODE_MASK,
+ 0);
+ if (error)
+ return error;
+
+ error = regmap_read(haptics->regmap, DA7280_MEM_CTL1, &val);
+ if (error)
+ return error;
+
+ return regmap_bulk_write(haptics->regmap, val, haptics->snp_mem,
+ DA7280_SNP_MEM_MAX - val + 1);
+}
+
+static int da7280_haptic_set_pwm(struct da7280_haptic *haptics, bool enabled)
+{
+ struct pwm_state state;
+ u64 period_mag_multi;
+ int error;
+
+ if (!haptics->gain && enabled) {
+ dev_err(haptics->dev, "Unable to enable pwm with 0 gain\n");
+ return -EINVAL;
+ }
+
+ pwm_get_state(haptics->pwm_dev, &state);
+ state.enabled = enabled;
+ if (enabled) {
+ period_mag_multi = (u64)state.period * haptics->gain;
+ period_mag_multi >>= DA7280_MAX_MAGNITUDE_SHIFT;
+
+ /*
+ * The interpretation of duty cycle depends on the acc_en,
+ * it should be between 50% and 100% for acc_en = 0.
+ * See datasheet 'PWM mode' section.
+ */
+ if (!haptics->acc_en) {
+ period_mag_multi += state.period;
+ period_mag_multi /= 2;
+ }
+
+ state.duty_cycle = period_mag_multi;
+ }
+
+ error = pwm_apply_might_sleep(haptics->pwm_dev, &state);
+ if (error)
+ dev_err(haptics->dev, "Failed to apply pwm state: %d\n", error);
+
+ return error;
+}
+
+static void da7280_haptic_activate(struct da7280_haptic *haptics)
+{
+ int error;
+
+ if (haptics->active)
+ return;
+
+ switch (haptics->op_mode) {
+ case DA7280_DRO_MODE:
+ /* the valid range check when acc_en is enabled */
+ if (haptics->acc_en && haptics->level > 0x7F)
+ haptics->level = 0x7F;
+ else if (haptics->level > 0xFF)
+ haptics->level = 0xFF;
+
+ /* Set level as a % of ACTUATOR_NOMMAX (nommax) */
+ error = regmap_write(haptics->regmap, DA7280_TOP_CTL2,
+ haptics->level);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to set level to %d: %d\n",
+ haptics->level, error);
+ return;
+ }
+ break;
+
+ case DA7280_PWM_MODE:
+ if (da7280_haptic_set_pwm(haptics, true))
+ return;
+ break;
+
+ case DA7280_RTWM_MODE:
+ /*
+ * The pattern will be played by the PS_SEQ_ID and the
+ * PS_SEQ_LOOP
+ */
+ break;
+
+ case DA7280_ETWM_MODE:
+ /*
+ * The pattern will be played by the GPI[N] state,
+ * GPI(N)_SEQUENCE_ID and the PS_SEQ_LOOP. See the
+ * datasheet for the details.
+ */
+ break;
+
+ default:
+ dev_err(haptics->dev, "Invalid op mode %d\n", haptics->op_mode);
+ return;
+ }
+
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_OPERATION_MODE_MASK,
+ haptics->op_mode);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to set operation mode: %d", error);
+ return;
+ }
+
+ if (haptics->op_mode == DA7280_PWM_MODE ||
+ haptics->op_mode == DA7280_RTWM_MODE) {
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_SEQ_START_MASK,
+ DA7280_SEQ_START_MASK);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to start sequence: %d\n", error);
+ return;
+ }
+ }
+
+ haptics->active = true;
+}
+
+static void da7280_haptic_deactivate(struct da7280_haptic *haptics)
+{
+ int error;
+
+ if (!haptics->active)
+ return;
+
+ /* Set to Inactive mode */
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_OPERATION_MODE_MASK, 0);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to clear operation mode: %d", error);
+ return;
+ }
+
+ switch (haptics->op_mode) {
+ case DA7280_DRO_MODE:
+ error = regmap_write(haptics->regmap,
+ DA7280_TOP_CTL2, 0);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to disable DRO mode: %d\n", error);
+ return;
+ }
+ break;
+
+ case DA7280_PWM_MODE:
+ if (da7280_haptic_set_pwm(haptics, false))
+ return;
+ break;
+
+ case DA7280_RTWM_MODE:
+ case DA7280_ETWM_MODE:
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_SEQ_START_MASK, 0);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to disable RTWM/ETWM mode: %d\n",
+ error);
+ return;
+ }
+ break;
+
+ default:
+ dev_err(haptics->dev, "Invalid op mode %d\n", haptics->op_mode);
+ return;
+ }
+
+ haptics->active = false;
+}
+
+static void da7280_haptic_work(struct work_struct *work)
+{
+ struct da7280_haptic *haptics =
+ container_of(work, struct da7280_haptic, work);
+ int val = haptics->val;
+
+ if (val)
+ da7280_haptic_activate(haptics);
+ else
+ da7280_haptic_deactivate(haptics);
+}
+
+static int da7280_haptics_upload_effect(struct input_dev *dev,
+ struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct da7280_haptic *haptics = input_get_drvdata(dev);
+ s16 data[DA7280_SNP_MEM_SIZE] = { 0 };
+ unsigned int val;
+ int tmp, i, num;
+ int error;
+
+ /* The effect should be uploaded when haptic is not working */
+ if (haptics->active)
+ return -EBUSY;
+
+ switch (effect->type) {
+ /* DRO/PWM modes support this type */
+ case FF_CONSTANT:
+ haptics->op_mode = haptics->const_op_mode;
+ if (haptics->op_mode == DA7280_DRO_MODE) {
+ tmp = effect->u.constant.level * 254;
+ haptics->level = tmp / 0x7FFF;
+ break;
+ }
+
+ haptics->gain = effect->u.constant.level <= 0 ?
+ 0 : effect->u.constant.level;
+ break;
+
+ /* RTWM/ETWM modes support this type */
+ case FF_PERIODIC:
+ if (effect->u.periodic.waveform != FF_CUSTOM) {
+ dev_err(haptics->dev,
+ "Device can only accept FF_CUSTOM waveform\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Load the data and check the length.
+ * the data will be patterns in this case: 4 < X <= 100,
+ * and will be saved into the waveform memory inside DA728x.
+ * If X = 2, the data will be PS_SEQ_ID and PS_SEQ_LOOP.
+ * If X = 3, the 1st data will be GPIX_SEQUENCE_ID .
+ */
+ if (effect->u.periodic.custom_len == DA7280_CUSTOM_DATA_LEN)
+ goto set_seq_id_loop;
+
+ if (effect->u.periodic.custom_len == DA7280_CUSTOM_GP_DATA_LEN)
+ goto set_gpix_seq_id;
+
+ if (effect->u.periodic.custom_len < DA7280_CUSTOM_DATA_LEN ||
+ effect->u.periodic.custom_len > DA7280_SNP_MEM_SIZE) {
+ dev_err(haptics->dev, "Invalid waveform data size\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(data, effect->u.periodic.custom_data,
+ sizeof(s16) *
+ effect->u.periodic.custom_len))
+ return -EFAULT;
+
+ memset(haptics->snp_mem, 0, DA7280_SNP_MEM_SIZE);
+
+ for (i = 0; i < effect->u.periodic.custom_len; i++) {
+ if (data[i] < 0 || data[i] > 0xff) {
+ dev_err(haptics->dev,
+ "Invalid waveform data %d at offset %d\n",
+ data[i], i);
+ return -EINVAL;
+ }
+ haptics->snp_mem[i] = (u8)data[i];
+ }
+
+ error = da7280_haptic_mem_update(haptics);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to upload waveform: %d\n", error);
+ return error;
+ }
+ break;
+
+set_seq_id_loop:
+ if (copy_from_user(data, effect->u.periodic.custom_data,
+ sizeof(s16) * DA7280_CUSTOM_DATA_LEN))
+ return -EFAULT;
+
+ if (data[DA7280_CUSTOM_SEQ_ID_IDX] < 0 ||
+ data[DA7280_CUSTOM_SEQ_ID_IDX] > DA7280_SEQ_ID_MAX ||
+ data[DA7280_CUSTOM_SEQ_LOOP_IDX] < 0 ||
+ data[DA7280_CUSTOM_SEQ_LOOP_IDX] > DA7280_SEQ_LOOP_MAX) {
+ dev_err(haptics->dev,
+ "Invalid custom id (%d) or loop (%d)\n",
+ data[DA7280_CUSTOM_SEQ_ID_IDX],
+ data[DA7280_CUSTOM_SEQ_LOOP_IDX]);
+ return -EINVAL;
+ }
+
+ haptics->ps_seq_id = data[DA7280_CUSTOM_SEQ_ID_IDX] & 0x0f;
+ haptics->ps_seq_loop = data[DA7280_CUSTOM_SEQ_LOOP_IDX] & 0x0f;
+ haptics->op_mode = haptics->periodic_op_mode;
+
+ val = FIELD_PREP(DA7280_PS_SEQ_ID_MASK, haptics->ps_seq_id) |
+ FIELD_PREP(DA7280_PS_SEQ_LOOP_MASK,
+ haptics->ps_seq_loop);
+ error = regmap_write(haptics->regmap, DA7280_SEQ_CTL2, val);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to update PS sequence: %d\n", error);
+ return error;
+ }
+ break;
+
+set_gpix_seq_id:
+ if (copy_from_user(data, effect->u.periodic.custom_data,
+ sizeof(s16) * DA7280_CUSTOM_GP_DATA_LEN))
+ return -EFAULT;
+
+ if (data[DA7280_CUSTOM_GPI_SEQ_ID_IDX] < 0 ||
+ data[DA7280_CUSTOM_GPI_SEQ_ID_IDX] > DA7280_SEQ_ID_MAX ||
+ data[DA7280_CUSTOM_GPI_NUM_IDX] < 0 ||
+ data[DA7280_CUSTOM_GPI_NUM_IDX] > DA7280_GPI_SEQ_ID_MAX) {
+ dev_err(haptics->dev,
+ "Invalid custom GPI id (%d) or num (%d)\n",
+ data[DA7280_CUSTOM_GPI_SEQ_ID_IDX],
+ data[DA7280_CUSTOM_GPI_NUM_IDX]);
+ return -EINVAL;
+ }
+
+ num = data[DA7280_CUSTOM_GPI_NUM_IDX] & 0x0f;
+ haptics->gpi_ctl[num].seq_id =
+ data[DA7280_CUSTOM_GPI_SEQ_ID_IDX] & 0x0f;
+ haptics->op_mode = haptics->periodic_op_mode;
+
+ val = FIELD_PREP(DA7280_GPI0_SEQUENCE_ID_MASK,
+ haptics->gpi_ctl[num].seq_id);
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_GPI_0_CTL + num,
+ DA7280_GPI0_SEQUENCE_ID_MASK,
+ val);
+ if (error) {
+ dev_err(haptics->dev,
+ "Failed to update GPI sequence: %d\n", error);
+ return error;
+ }
+ break;
+
+ default:
+ dev_err(haptics->dev, "Unsupported effect type: %d\n",
+ effect->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da7280_haptics_playback(struct input_dev *dev,
+ int effect_id, int val)
+{
+ struct da7280_haptic *haptics = input_get_drvdata(dev);
+
+ if (!haptics->op_mode) {
+ dev_warn(haptics->dev, "No effects have been uploaded\n");
+ return -EINVAL;
+ }
+
+ if (likely(!haptics->suspended)) {
+ haptics->val = val;
+ schedule_work(&haptics->work);
+ }
+
+ return 0;
+}
+
+static int da7280_haptic_start(struct da7280_haptic *haptics)
+{
+ int error;
+
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_STANDBY_EN_MASK,
+ DA7280_STANDBY_EN_MASK);
+ if (error) {
+ dev_err(haptics->dev, "Unable to enable device: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void da7280_haptic_stop(struct da7280_haptic *haptics)
+{
+ int error;
+
+ cancel_work_sync(&haptics->work);
+
+
+ da7280_haptic_deactivate(haptics);
+
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_CTL1,
+ DA7280_STANDBY_EN_MASK, 0);
+ if (error)
+ dev_err(haptics->dev, "Failed to disable device: %d\n", error);
+}
+
+static int da7280_haptic_open(struct input_dev *dev)
+{
+ struct da7280_haptic *haptics = input_get_drvdata(dev);
+
+ return da7280_haptic_start(haptics);
+}
+
+static void da7280_haptic_close(struct input_dev *dev)
+{
+ struct da7280_haptic *haptics = input_get_drvdata(dev);
+
+ da7280_haptic_stop(haptics);
+}
+
+static u8 da7280_haptic_of_mode_str(struct device *dev,
+ const char *str)
+{
+ if (!strcmp(str, "LRA")) {
+ return DA7280_LRA;
+ } else if (!strcmp(str, "ERM-bar")) {
+ return DA7280_ERM_BAR;
+ } else if (!strcmp(str, "ERM-coin")) {
+ return DA7280_ERM_COIN;
+ } else {
+ dev_warn(dev, "Invalid string - set to LRA\n");
+ return DA7280_LRA;
+ }
+}
+
+static u8 da7280_haptic_of_gpi_mode_str(struct device *dev,
+ const char *str)
+{
+ if (!strcmp(str, "Single-pattern")) {
+ return 0;
+ } else if (!strcmp(str, "Multi-pattern")) {
+ return 1;
+ } else {
+ dev_warn(dev, "Invalid string - set to Single-pattern\n");
+ return 0;
+ }
+}
+
+static u8 da7280_haptic_of_gpi_pol_str(struct device *dev,
+ const char *str)
+{
+ if (!strcmp(str, "Rising-edge")) {
+ return 0;
+ } else if (!strcmp(str, "Falling-edge")) {
+ return 1;
+ } else if (!strcmp(str, "Both-edge")) {
+ return 2;
+ } else {
+ dev_warn(dev, "Invalid string - set to Rising-edge\n");
+ return 0;
+ }
+}
+
+static u8 da7280_haptic_of_volt_rating_set(u32 val)
+{
+ u32 voltage = val / DA7280_VOLTAGE_RATE_STEP + 1;
+
+ return min_t(u32, voltage, 0xff);
+}
+
+static void da7280_parse_properties(struct device *dev,
+ struct da7280_haptic *haptics)
+{
+ unsigned int i, mem[DA7280_SNP_MEM_SIZE];
+ char gpi_str1[] = "dlg,gpi0-seq-id";
+ char gpi_str2[] = "dlg,gpi0-mode";
+ char gpi_str3[] = "dlg,gpi0-polarity";
+ const char *str;
+ u32 val;
+ int error;
+
+ /*
+ * If there is no property, then use the mode programmed into the chip.
+ */
+ haptics->dev_type = DA7280_DEV_MAX;
+ error = device_property_read_string(dev, "dlg,actuator-type", &str);
+ if (!error)
+ haptics->dev_type = da7280_haptic_of_mode_str(dev, str);
+
+ haptics->const_op_mode = DA7280_DRO_MODE;
+ error = device_property_read_u32(dev, "dlg,const-op-mode", &val);
+ if (!error && val == DA7280_FF_PERIODIC_PWM)
+ haptics->const_op_mode = DA7280_PWM_MODE;
+
+ haptics->periodic_op_mode = DA7280_RTWM_MODE;
+ error = device_property_read_u32(dev, "dlg,periodic-op-mode", &val);
+ if (!error && val == DA7280_FF_PERIODIC_ETWM)
+ haptics->periodic_op_mode = DA7280_ETWM_MODE;
+
+ haptics->nommax = DA7280_SKIP_INIT;
+ error = device_property_read_u32(dev, "dlg,nom-microvolt", &val);
+ if (!error && val < DA7280_VOLTAGE_RATE_MAX)
+ haptics->nommax = da7280_haptic_of_volt_rating_set(val);
+
+ haptics->absmax = DA7280_SKIP_INIT;
+ error = device_property_read_u32(dev, "dlg,abs-max-microvolt", &val);
+ if (!error && val < DA7280_VOLTAGE_RATE_MAX)
+ haptics->absmax = da7280_haptic_of_volt_rating_set(val);
+
+ haptics->imax = DA7280_IMAX_DEFAULT;
+ error = device_property_read_u32(dev, "dlg,imax-microamp", &val);
+ if (!error && val < DA7280_IMAX_LIMIT)
+ haptics->imax = (val - 28600) / DA7280_IMAX_STEP + 1;
+
+ haptics->impd = DA7280_IMPD_DEFAULT;
+ error = device_property_read_u32(dev, "dlg,impd-micro-ohms", &val);
+ if (!error && val <= DA7280_IMPD_MAX)
+ haptics->impd = val;
+
+ haptics->resonant_freq_h = DA7280_SKIP_INIT;
+ haptics->resonant_freq_l = DA7280_SKIP_INIT;
+ error = device_property_read_u32(dev, "dlg,resonant-freq-hz", &val);
+ if (!error) {
+ if (val < DA7280_MAX_RESONAT_FREQ_HZ &&
+ val > DA7280_MIN_RESONAT_FREQ_HZ) {
+ haptics->resonant_freq_h =
+ ((1000000000 / (val * 1333)) >> 7) & 0xFF;
+ haptics->resonant_freq_l =
+ (1000000000 / (val * 1333)) & 0x7F;
+ } else {
+ haptics->resonant_freq_h = DA7280_RESONT_FREQH_DFT;
+ haptics->resonant_freq_l = DA7280_RESONT_FREQL_DFT;
+ }
+ }
+
+ /* If no property, set to zero as default is to do nothing. */
+ haptics->ps_seq_id = 0;
+ error = device_property_read_u32(dev, "dlg,ps-seq-id", &val);
+ if (!error && val <= DA7280_SEQ_ID_MAX)
+ haptics->ps_seq_id = val;
+
+ haptics->ps_seq_loop = 0;
+ error = device_property_read_u32(dev, "dlg,ps-seq-loop", &val);
+ if (!error && val <= DA7280_SEQ_LOOP_MAX)
+ haptics->ps_seq_loop = val;
+
+ /* GPI0~2 Control */
+ for (i = 0; i <= DA7280_GPI_SEQ_ID_MAX; i++) {
+ gpi_str1[7] = '0' + i;
+ haptics->gpi_ctl[i].seq_id = DA7280_GPI_SEQ_ID_DFT + i;
+ error = device_property_read_u32 (dev, gpi_str1, &val);
+ if (!error && val <= DA7280_SEQ_ID_MAX)
+ haptics->gpi_ctl[i].seq_id = val;
+
+ gpi_str2[7] = '0' + i;
+ haptics->gpi_ctl[i].mode = 0;
+ error = device_property_read_string(dev, gpi_str2, &str);
+ if (!error)
+ haptics->gpi_ctl[i].mode =
+ da7280_haptic_of_gpi_mode_str(dev, str);
+
+ gpi_str3[7] = '0' + i;
+ haptics->gpi_ctl[i].polarity = 0;
+ error = device_property_read_string(dev, gpi_str3, &str);
+ if (!error)
+ haptics->gpi_ctl[i].polarity =
+ da7280_haptic_of_gpi_pol_str(dev, str);
+ }
+
+ haptics->bemf_sense_en =
+ device_property_read_bool(dev, "dlg,bemf-sens-enable");
+ haptics->freq_track_en =
+ device_property_read_bool(dev, "dlg,freq-track-enable");
+ haptics->acc_en =
+ device_property_read_bool(dev, "dlg,acc-enable");
+ haptics->rapid_stop_en =
+ device_property_read_bool(dev, "dlg,rapid-stop-enable");
+ haptics->amp_pid_en =
+ device_property_read_bool(dev, "dlg,amp-pid-enable");
+
+ haptics->mem_update = false;
+ error = device_property_read_u32_array(dev, "dlg,mem-array",
+ &mem[0], DA7280_SNP_MEM_SIZE);
+ if (!error) {
+ haptics->mem_update = true;
+ memset(haptics->snp_mem, 0, DA7280_SNP_MEM_SIZE);
+ for (i = 0; i < DA7280_SNP_MEM_SIZE; i++) {
+ if (mem[i] <= 0xff) {
+ haptics->snp_mem[i] = (u8)mem[i];
+ } else {
+ dev_err(haptics->dev,
+ "Invalid data in mem-array at %d: %x\n",
+ i, mem[i]);
+ haptics->mem_update = false;
+ break;
+ }
+ }
+ }
+}
+
+static irqreturn_t da7280_irq_handler(int irq, void *data)
+{
+ struct da7280_haptic *haptics = data;
+ struct device *dev = haptics->dev;
+ u8 events[DA7280_IRQ_NUM];
+ int error;
+
+ /* Check what events have happened */
+ error = regmap_bulk_read(haptics->regmap, DA7280_IRQ_EVENT1,
+ events, sizeof(events));
+ if (error) {
+ dev_err(dev, "failed to read interrupt data: %d\n", error);
+ goto out;
+ }
+
+ /* Clear events */
+ error = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, events[0]);
+ if (error) {
+ dev_err(dev, "failed to clear interrupts: %d\n", error);
+ goto out;
+ }
+
+ if (events[0] & DA7280_E_SEQ_FAULT_MASK) {
+ /*
+ * Stop first if haptic is active, otherwise, the fault may
+ * happen continually even though the bit is cleared.
+ */
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_CTL1,
+ DA7280_OPERATION_MODE_MASK, 0);
+ if (error)
+ dev_err(dev, "failed to clear op mode on fault: %d\n",
+ error);
+ }
+
+ if (events[0] & DA7280_E_SEQ_DONE_MASK)
+ haptics->active = false;
+
+ if (events[0] & DA7280_E_WARNING_MASK) {
+ if (events[1] & DA7280_E_LIM_DRIVE_MASK ||
+ events[1] & DA7280_E_LIM_DRIVE_ACC_MASK)
+ dev_warn(dev, "Please reduce the driver level\n");
+ if (events[1] & DA7280_E_MEM_TYPE_MASK)
+ dev_warn(dev, "Please check the mem data format\n");
+ if (events[1] & DA7280_E_OVERTEMP_WARN_MASK)
+ dev_warn(dev, "Over-temperature warning\n");
+ }
+
+ if (events[0] & DA7280_E_SEQ_FAULT_MASK) {
+ if (events[2] & DA7280_E_SEQ_ID_FAULT_MASK)
+ dev_info(dev, "Please reload PS_SEQ_ID & mem data\n");
+ if (events[2] & DA7280_E_MEM_FAULT_MASK)
+ dev_info(dev, "Please reload the mem data\n");
+ if (events[2] & DA7280_E_PWM_FAULT_MASK)
+ dev_info(dev, "Please restart PWM interface\n");
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int da7280_init(struct da7280_haptic *haptics)
+{
+ unsigned int val = 0;
+ u32 v2i_factor;
+ int error, i;
+ u8 mask = 0;
+
+ /*
+ * If device type is DA7280_DEV_MAX then simply use currently
+ * programmed mode.
+ */
+ if (haptics->dev_type == DA7280_DEV_MAX) {
+ error = regmap_read(haptics->regmap, DA7280_TOP_CFG1, &val);
+ if (error)
+ goto out_err;
+
+ haptics->dev_type = val & DA7280_ACTUATOR_TYPE_MASK ?
+ DA7280_ERM_COIN : DA7280_LRA;
+ }
+
+ /* Apply user settings */
+ if (haptics->dev_type == DA7280_LRA &&
+ haptics->resonant_freq_l != DA7280_SKIP_INIT) {
+ error = regmap_write(haptics->regmap, DA7280_FRQ_LRA_PER_H,
+ haptics->resonant_freq_h);
+ if (error)
+ goto out_err;
+ error = regmap_write(haptics->regmap, DA7280_FRQ_LRA_PER_L,
+ haptics->resonant_freq_l);
+ if (error)
+ goto out_err;
+ } else if (haptics->dev_type == DA7280_ERM_COIN) {
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_INT_CFG1,
+ DA7280_BEMF_FAULT_LIM_MASK, 0);
+ if (error)
+ goto out_err;
+
+ mask = DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+ DA7280_V2I_FACTOR_FREEZE_MASK;
+ val = DA7280_TST_CALIB_IMPEDANCE_DIS_MASK |
+ DA7280_V2I_FACTOR_FREEZE_MASK;
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_CFG4,
+ mask, val);
+ if (error)
+ goto out_err;
+
+ haptics->acc_en = false;
+ haptics->rapid_stop_en = false;
+ haptics->amp_pid_en = false;
+ }
+
+ mask = DA7280_ACTUATOR_TYPE_MASK |
+ DA7280_BEMF_SENSE_EN_MASK |
+ DA7280_FREQ_TRACK_EN_MASK |
+ DA7280_ACCELERATION_EN_MASK |
+ DA7280_RAPID_STOP_EN_MASK |
+ DA7280_AMP_PID_EN_MASK;
+ val = FIELD_PREP(DA7280_ACTUATOR_TYPE_MASK,
+ (haptics->dev_type ? 1 : 0)) |
+ FIELD_PREP(DA7280_BEMF_SENSE_EN_MASK,
+ (haptics->bemf_sense_en ? 1 : 0)) |
+ FIELD_PREP(DA7280_FREQ_TRACK_EN_MASK,
+ (haptics->freq_track_en ? 1 : 0)) |
+ FIELD_PREP(DA7280_ACCELERATION_EN_MASK,
+ (haptics->acc_en ? 1 : 0)) |
+ FIELD_PREP(DA7280_RAPID_STOP_EN_MASK,
+ (haptics->rapid_stop_en ? 1 : 0)) |
+ FIELD_PREP(DA7280_AMP_PID_EN_MASK,
+ (haptics->amp_pid_en ? 1 : 0));
+
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_CFG1, mask, val);
+ if (error)
+ goto out_err;
+
+ error = regmap_update_bits(haptics->regmap, DA7280_TOP_CFG5,
+ DA7280_V2I_FACTOR_OFFSET_EN_MASK,
+ haptics->acc_en ?
+ DA7280_V2I_FACTOR_OFFSET_EN_MASK : 0);
+ if (error)
+ goto out_err;
+
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CFG2,
+ DA7280_MEM_DATA_SIGNED_MASK,
+ haptics->acc_en ?
+ 0 : DA7280_MEM_DATA_SIGNED_MASK);
+ if (error)
+ goto out_err;
+
+ if (haptics->nommax != DA7280_SKIP_INIT) {
+ error = regmap_write(haptics->regmap, DA7280_ACTUATOR1,
+ haptics->nommax);
+ if (error)
+ goto out_err;
+ }
+
+ if (haptics->absmax != DA7280_SKIP_INIT) {
+ error = regmap_write(haptics->regmap, DA7280_ACTUATOR2,
+ haptics->absmax);
+ if (error)
+ goto out_err;
+ }
+
+ error = regmap_update_bits(haptics->regmap, DA7280_ACTUATOR3,
+ DA7280_IMAX_MASK, haptics->imax);
+ if (error)
+ goto out_err;
+
+ v2i_factor = haptics->impd * (haptics->imax + 4) / 1610400;
+ error = regmap_write(haptics->regmap, DA7280_CALIB_V2I_L,
+ v2i_factor & 0xff);
+ if (error)
+ goto out_err;
+ error = regmap_write(haptics->regmap, DA7280_CALIB_V2I_H,
+ v2i_factor >> 8);
+ if (error)
+ goto out_err;
+
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_TOP_CTL1,
+ DA7280_STANDBY_EN_MASK,
+ DA7280_STANDBY_EN_MASK);
+ if (error)
+ goto out_err;
+
+ if (haptics->mem_update) {
+ error = da7280_haptic_mem_update(haptics);
+ if (error)
+ goto out_err;
+ }
+
+ /* Set PS_SEQ_ID and PS_SEQ_LOOP */
+ val = FIELD_PREP(DA7280_PS_SEQ_ID_MASK, haptics->ps_seq_id) |
+ FIELD_PREP(DA7280_PS_SEQ_LOOP_MASK, haptics->ps_seq_loop);
+ error = regmap_write(haptics->regmap, DA7280_SEQ_CTL2, val);
+ if (error)
+ goto out_err;
+
+ /* GPI(N) CTL */
+ for (i = 0; i < 3; i++) {
+ val = FIELD_PREP(DA7280_GPI0_SEQUENCE_ID_MASK,
+ haptics->gpi_ctl[i].seq_id) |
+ FIELD_PREP(DA7280_GPI0_MODE_MASK,
+ haptics->gpi_ctl[i].mode) |
+ FIELD_PREP(DA7280_GPI0_POLARITY_MASK,
+ haptics->gpi_ctl[i].polarity);
+ error = regmap_write(haptics->regmap,
+ DA7280_GPI_0_CTL + i, val);
+ if (error)
+ goto out_err;
+ }
+
+ /* Mask ADC_SAT_M bit as default */
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_IRQ_MASK2,
+ DA7280_ADC_SAT_M_MASK,
+ DA7280_ADC_SAT_M_MASK);
+ if (error)
+ goto out_err;
+
+ /* Clear Interrupts */
+ error = regmap_write(haptics->regmap, DA7280_IRQ_EVENT1, 0xff);
+ if (error)
+ goto out_err;
+
+ error = regmap_update_bits(haptics->regmap,
+ DA7280_IRQ_MASK1,
+ DA7280_SEQ_FAULT_M_MASK |
+ DA7280_SEQ_DONE_M_MASK,
+ 0);
+ if (error)
+ goto out_err;
+
+ haptics->active = false;
+ return 0;
+
+out_err:
+ dev_err(haptics->dev, "chip initialization error: %d\n", error);
+ return error;
+}
+
+static int da7280_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct da7280_haptic *haptics;
+ struct input_dev *input_dev;
+ struct pwm_state state;
+ struct ff_device *ff;
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL);
+ if (!haptics)
+ return -ENOMEM;
+
+ haptics->dev = dev;
+
+ da7280_parse_properties(dev, haptics);
+
+ if (haptics->const_op_mode == DA7280_PWM_MODE) {
+ haptics->pwm_dev = devm_pwm_get(dev, NULL);
+ error = PTR_ERR_OR_ZERO(haptics->pwm_dev);
+ if (error) {
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Unable to request PWM: %d\n",
+ error);
+ return error;
+ }
+
+ /* Sync up PWM state and ensure it is off. */
+ pwm_init_state(haptics->pwm_dev, &state);
+ state.enabled = false;
+ error = pwm_apply_might_sleep(haptics->pwm_dev, &state);
+ if (error) {
+ dev_err(dev, "Failed to apply PWM state: %d\n", error);
+ return error;
+ }
+
+ /*
+ * Check PWM period, PWM freq = 1000000 / state.period.
+ * The valid PWM freq range: 10k ~ 250kHz.
+ */
+ if (state.period > 100000 || state.period < 4000) {
+ dev_err(dev, "Unsupported PWM period: %lld\n",
+ state.period);
+ return -EINVAL;
+ }
+ }
+
+ INIT_WORK(&haptics->work, da7280_haptic_work);
+
+ haptics->client = client;
+ i2c_set_clientdata(client, haptics);
+
+ haptics->regmap = devm_regmap_init_i2c(client,
+ &da7280_haptic_regmap_config);
+ error = PTR_ERR_OR_ZERO(haptics->regmap);
+ if (error) {
+ dev_err(dev, "Failed to allocate register map: %d\n", error);
+ return error;
+ }
+
+ error = da7280_init(haptics);
+ if (error) {
+ dev_err(dev, "Failed to initialize device: %d\n", error);
+ return error;
+ }
+
+ /* Initialize input device for haptic device */
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ dev_err(dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "da7280-haptic";
+ input_dev->dev.parent = client->dev.parent;
+ input_dev->open = da7280_haptic_open;
+ input_dev->close = da7280_haptic_close;
+ input_set_drvdata(input_dev, haptics);
+ haptics->input_dev = input_dev;
+
+ input_set_capability(haptics->input_dev, EV_FF, FF_PERIODIC);
+ input_set_capability(haptics->input_dev, EV_FF, FF_CUSTOM);
+ input_set_capability(haptics->input_dev, EV_FF, FF_CONSTANT);
+ input_set_capability(haptics->input_dev, EV_FF, FF_GAIN);
+
+ error = input_ff_create(haptics->input_dev,
+ DA7280_FF_EFFECT_COUNT_MAX);
+ if (error) {
+ dev_err(dev, "Failed to create FF input device: %d\n", error);
+ return error;
+ }
+
+ ff = input_dev->ff;
+ ff->upload = da7280_haptics_upload_effect;
+ ff->playback = da7280_haptics_playback;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, client->irq,
+ NULL, da7280_irq_handler,
+ IRQF_ONESHOT,
+ "da7280-haptics", haptics);
+ if (error) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n",
+ client->irq, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int da7280_suspend(struct device *dev)
+{
+ struct da7280_haptic *haptics = dev_get_drvdata(dev);
+
+ guard(mutex)(&haptics->input_dev->mutex);
+
+ /*
+ * Make sure no new requests will be submitted while device is
+ * suspended.
+ */
+ scoped_guard(spinlock_irq, &haptics->input_dev->event_lock) {
+ haptics->suspended = true;
+ }
+
+ da7280_haptic_stop(haptics);
+
+ return 0;
+}
+
+static int da7280_resume(struct device *dev)
+{
+ struct da7280_haptic *haptics = dev_get_drvdata(dev);
+ int error;
+
+ guard(mutex)(&haptics->input_dev->mutex);
+
+ error = da7280_haptic_start(haptics);
+ if (error)
+ return error;
+
+ scoped_guard(spinlock_irq, &haptics->input_dev->event_lock) {
+ haptics->suspended = false;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id da7280_of_match[] = {
+ { .compatible = "dlg,da7280", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7280_of_match);
+#endif
+
+static const struct i2c_device_id da7280_i2c_id[] = {
+ { "da7280", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7280_i2c_id);
+
+static DEFINE_SIMPLE_DEV_PM_OPS(da7280_pm_ops, da7280_suspend, da7280_resume);
+
+static struct i2c_driver da7280_driver = {
+ .driver = {
+ .name = "da7280",
+ .of_match_table = of_match_ptr(da7280_of_match),
+ .pm = pm_sleep_ptr(&da7280_pm_ops),
+ },
+ .probe = da7280_probe,
+ .id_table = da7280_i2c_id,
+};
+module_i2c_driver(da7280_driver);
+
+MODULE_DESCRIPTION("DA7280 haptics driver");
+MODULE_AUTHOR("Roy Im <Roy.Im.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
index 266e07fdc182..cc23625019e3 100644
--- a/drivers/input/misc/da9052_onkey.c
+++ b/drivers/input/misc/da9052_onkey.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ON pin driver for Dialog DA9052 PMICs
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/input.h>
@@ -131,7 +127,7 @@ err_free_mem:
return error;
}
-static int da9052_onkey_remove(struct platform_device *pdev)
+static void da9052_onkey_remove(struct platform_device *pdev)
{
struct da9052_onkey *onkey = platform_get_drvdata(pdev);
@@ -140,14 +136,12 @@ static int da9052_onkey_remove(struct platform_device *pdev)
input_unregister_device(onkey->input);
kfree(onkey);
-
- return 0;
}
static struct platform_driver da9052_onkey_driver = {
.probe = da9052_onkey_probe,
.remove = da9052_onkey_remove,
- .driver = {
+ .driver = {
.name = "da9052-onkey",
},
};
diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c
index 3251a9693f45..3e4fc2f01011 100644
--- a/drivers/input/misc/da9055_onkey.c
+++ b/drivers/input/misc/da9055_onkey.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ON pin driver for Dialog DA9055 PMICs
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/input.h>
@@ -80,11 +76,8 @@ static int da9055_onkey_probe(struct platform_device *pdev)
int irq, err;
irq = platform_get_irq_byname(pdev, "ONKEY");
- if (irq < 0) {
- dev_err(&pdev->dev,
- "Failed to get an IRQ for input device, %d\n", irq);
+ if (irq < 0)
return -EINVAL;
- }
onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
if (!onkey) {
@@ -139,7 +132,7 @@ err_free_input:
return err;
}
-static int da9055_onkey_remove(struct platform_device *pdev)
+static void da9055_onkey_remove(struct platform_device *pdev)
{
struct da9055_onkey *onkey = platform_get_drvdata(pdev);
int irq = platform_get_irq_byname(pdev, "ONKEY");
@@ -148,14 +141,12 @@ static int da9055_onkey_remove(struct platform_device *pdev)
free_irq(irq, onkey);
cancel_delayed_work_sync(&onkey->work);
input_unregister_device(onkey->input);
-
- return 0;
}
static struct platform_driver da9055_onkey_driver = {
.probe = da9055_onkey_probe,
.remove = da9055_onkey_remove,
- .driver = {
+ .driver = {
.name = "da9055-onkey",
},
};
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
index 3e9c353d82ef..c338765e0ecd 100644
--- a/drivers/input/misc/da9063_onkey.c
+++ b/drivers/input/misc/da9063_onkey.c
@@ -1,28 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OnKey device driver for DA9063, DA9062 and DA9061 PMICs
* Copyright (C) 2015 Dialog Semiconductor Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/property.h>
#include <linux/workqueue.h>
#include <linux/regmap.h>
-#include <linux/of.h>
#include <linux/mfd/da9063/core.h>
-#include <linux/mfd/da9063/pdata.h>
#include <linux/mfd/da9063/registers.h>
#include <linux/mfd/da9062/core.h>
#include <linux/mfd/da9062/registers.h>
@@ -82,13 +75,6 @@ static const struct da906x_chip_config da9062_regs = {
.name = "da9062-onkey",
};
-static const struct of_device_id da9063_compatible_reg_id_table[] = {
- { .compatible = "dlg,da9063-onkey", .data = &da9063_regs },
- { .compatible = "dlg,da9062-onkey", .data = &da9062_regs },
- { },
-};
-MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table);
-
static void da9063_poll_on(struct work_struct *work)
{
struct da9063_onkey *onkey = container_of(work,
@@ -192,104 +178,81 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static void da9063_cancel_poll(void *data)
-{
- struct da9063_onkey *onkey = data;
-
- cancel_delayed_work_sync(&onkey->work);
-}
-
static int da9063_onkey_probe(struct platform_device *pdev)
{
- struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
- struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
struct da9063_onkey *onkey;
- const struct of_device_id *match;
- int irq;
int error;
-
- match = of_match_node(da9063_compatible_reg_id_table,
- pdev->dev.of_node);
- if (!match)
- return -ENXIO;
+ int irq;
onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
GFP_KERNEL);
- if (!onkey) {
- dev_err(&pdev->dev, "Failed to allocate memory.\n");
+ if (!onkey)
return -ENOMEM;
- }
- onkey->config = match->data;
+ onkey->config = device_get_match_data(&pdev->dev);
+ if (!onkey->config)
+ return -ENXIO;
+
onkey->dev = &pdev->dev;
onkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!onkey->regmap) {
- dev_err(&pdev->dev, "Parent regmap unavailable.\n");
- return -ENXIO;
- }
+ if (!onkey->regmap)
+ return dev_err_probe(&pdev->dev, -ENXIO,
+ "Parent regmap unavailable.\n");
- if (pdata)
- onkey->key_power = pdata->key_power;
- else
- onkey->key_power =
- !of_property_read_bool(pdev->dev.of_node,
- "dlg,disable-key-power");
+ onkey->key_power = !device_property_read_bool(&pdev->dev,
+ "dlg,disable-key-power");
onkey->input = devm_input_allocate_device(&pdev->dev);
- if (!onkey->input) {
- dev_err(&pdev->dev, "Failed to allocated input device.\n");
+ if (!onkey->input)
return -ENOMEM;
- }
onkey->input->name = onkey->config->name;
snprintf(onkey->phys, sizeof(onkey->phys), "%s/input0",
onkey->config->name);
onkey->input->phys = onkey->phys;
- onkey->input->dev.parent = &pdev->dev;
-
- if (onkey->key_power)
- input_set_capability(onkey->input, EV_KEY, KEY_POWER);
- input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
+ input_set_capability(onkey->input, EV_KEY, KEY_POWER);
- INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
-
- error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey);
- if (error) {
- dev_err(&pdev->dev,
- "Failed to add cancel poll action: %d\n",
- error);
+ error = devm_delayed_work_autocancel(&pdev->dev, &onkey->work,
+ da9063_poll_on);
+ if (error)
return error;
- }
irq = platform_get_irq_byname(pdev, "ONKEY");
- if (irq < 0) {
- error = irq;
- dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error);
- return error;
- }
+ if (irq < 0)
+ return irq;
error = devm_request_threaded_irq(&pdev->dev, irq,
NULL, da9063_onkey_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"ONKEY", onkey);
- if (error) {
- dev_err(&pdev->dev,
- "Failed to request IRQ %d: %d\n", irq, error);
- return error;
- }
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "Failed to allocate onkey IRQ\n");
+
+ error = dev_pm_set_wake_irq(&pdev->dev, irq);
+ if (error)
+ dev_warn(&pdev->dev,
+ "Failed to set IRQ %d as a wake IRQ: %d\n",
+ irq, error);
+ else
+ device_init_wakeup(&pdev->dev, true);
error = input_register_device(onkey->input);
- if (error) {
- dev_err(&pdev->dev,
- "Failed to register input device: %d\n", error);
+ if (error)
return error;
- }
return 0;
}
+static const struct of_device_id da9063_compatible_reg_id_table[] = {
+ { .compatible = "dlg,da9063-onkey", .data = &da9063_regs },
+ { .compatible = "dlg,da9062-onkey", .data = &da9062_regs },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table);
+
static struct platform_driver da9063_onkey_driver = {
.probe = da9063_onkey_probe,
.driver = {
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
deleted file mode 100644
index c803db64a376..000000000000
--- a/drivers/input/misc/dm355evm_keys.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
- *
- * Copyright (c) 2008 by David Brownell
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/input/sparse-keymap.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-
-#include <linux/mfd/dm355evm_msp.h>
-#include <linux/module.h>
-
-
-/*
- * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
- * and an IR receptor used for the remote control. When any key is
- * pressed, or its autorepeat kicks in, an event is sent. This driver
- * read those events from the small (32 event) queue and reports them.
- *
- * Note that physically there can only be one of these devices.
- *
- * This driver was tested with firmware revision A4.
- */
-struct dm355evm_keys {
- struct input_dev *input;
- struct device *dev;
-};
-
-/* These initial keycodes can be remapped */
-static const struct key_entry dm355evm_keys[] = {
- /*
- * Pushbuttons on the EVM board ... note that the labels for these
- * are SW10/SW11/etc on the PC board. The left/right orientation
- * comes only from the firmware's documentation, and presumes the
- * power connector is immediately in front of you and the IR sensor
- * is to the right. (That is, rotate the board counter-clockwise
- * by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
- */
- { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */
- { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */
- { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */
- { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */
- { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */
-
- /*
- * IR buttons ... codes assigned to match the universal remote
- * provided with the EVM (Philips PM4S) using DVD code 0020.
- *
- * These event codes match firmware documentation, but other
- * remote controls could easily send more RC5-encoded events.
- * The PM4S manual was used in several cases to help select
- * a keycode reflecting the intended usage.
- *
- * RC5 codes are 14 bits, with two start bits (0x3 prefix)
- * and a toggle bit (masked out below).
- */
- { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */
- { KE_KEY, 0x3000, { KEY_NUMERIC_0 } },
- { KE_KEY, 0x3001, { KEY_NUMERIC_1 } },
- { KE_KEY, 0x3002, { KEY_NUMERIC_2 } },
- { KE_KEY, 0x3003, { KEY_NUMERIC_3 } },
- { KE_KEY, 0x3004, { KEY_NUMERIC_4 } },
- { KE_KEY, 0x3005, { KEY_NUMERIC_5 } },
- { KE_KEY, 0x3006, { KEY_NUMERIC_6 } },
- { KE_KEY, 0x3007, { KEY_NUMERIC_7 } },
- { KE_KEY, 0x3008, { KEY_NUMERIC_8 } },
- { KE_KEY, 0x3009, { KEY_NUMERIC_9 } },
- { KE_KEY, 0x3022, { KEY_ENTER } },
- { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */
- { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */
- { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */
- { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */
- { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */
- { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */
- { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */
- { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */
- { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */
- { KE_KEY, 0x3022, { KEY_PREVIOUS } },
- { KE_KEY, 0x3026, { KEY_SLEEP } },
- { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */
- { KE_KEY, 0x3175, { KEY_PLAY } },
- { KE_KEY, 0x3174, { KEY_FASTFORWARD } },
- { KE_KEY, 0x3177, { KEY_RECORD } },
- { KE_KEY, 0x3176, { KEY_STOP } },
- { KE_KEY, 0x3169, { KEY_PAUSE } },
-};
-
-/*
- * Because we communicate with the MSP430 using I2C, and all I2C calls
- * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
- * active low, but we go through the GPIO controller so we can trigger
- * on falling edges and not worry about enabling/disabling the IRQ in
- * the keypress handling path.
- */
-static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
-{
- static u16 last_event;
- struct dm355evm_keys *keys = _keys;
- const struct key_entry *ke;
- unsigned int keycode;
- int status;
- u16 event;
-
- /* For simplicity we ignore INPUT_COUNT and just read
- * events until we get the "queue empty" indicator.
- * Reading INPUT_LOW decrements the count.
- */
- for (;;) {
- status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
- if (status < 0) {
- dev_dbg(keys->dev, "input high err %d\n",
- status);
- break;
- }
- event = status << 8;
-
- status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
- if (status < 0) {
- dev_dbg(keys->dev, "input low err %d\n",
- status);
- break;
- }
- event |= status;
- if (event == 0xdead)
- break;
-
- /* Press and release a button: two events, same code.
- * Press and hold (autorepeat), then release: N events
- * (N > 2), same code. For RC5 buttons the toggle bits
- * distinguish (for example) "1-autorepeat" from "1 1";
- * but PCB buttons don't support that bit.
- *
- * So we must synthesize release events. We do that by
- * mapping events to a press/release event pair; then
- * to avoid adding extra events, skip the second event
- * of each pair.
- */
- if (event == last_event) {
- last_event = 0;
- continue;
- }
- last_event = event;
-
- /* ignore the RC5 toggle bit */
- event &= ~0x0800;
-
- /* find the key, or report it as unknown */
- ke = sparse_keymap_entry_from_scancode(keys->input, event);
- keycode = ke ? ke->keycode : KEY_UNKNOWN;
- dev_dbg(keys->dev,
- "input event 0x%04x--> keycode %d\n",
- event, keycode);
-
- /* report press + release */
- input_report_key(keys->input, keycode, 1);
- input_sync(keys->input);
- input_report_key(keys->input, keycode, 0);
- input_sync(keys->input);
- }
-
- return IRQ_HANDLED;
-}
-
-/*----------------------------------------------------------------------*/
-
-static int dm355evm_keys_probe(struct platform_device *pdev)
-{
- struct dm355evm_keys *keys;
- struct input_dev *input;
- int irq;
- int error;
-
- keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL);
- if (!keys)
- return -ENOMEM;
-
- input = devm_input_allocate_device(&pdev->dev);
- if (!input)
- return -ENOMEM;
-
- keys->dev = &pdev->dev;
- keys->input = input;
-
- input->name = "DM355 EVM Controls";
- input->phys = "dm355evm/input0";
-
- input->id.bustype = BUS_I2C;
- input->id.product = 0x0355;
- input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
-
- error = sparse_keymap_setup(input, dm355evm_keys, NULL);
- if (error)
- return error;
-
- /* REVISIT: flush the event queue? */
-
- /* set up "threaded IRQ handler" */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- error = devm_request_threaded_irq(&pdev->dev, irq,
- NULL, dm355evm_keys_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(&pdev->dev), keys);
- if (error)
- return error;
-
- /* register */
- error = input_register_device(input);
- if (error)
- return error;
-
- return 0;
-}
-
-/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should
- * be able to wake up the system. When device_may_wakeup(&pdev->dev), call
- * enable_irq_wake() on suspend, and disable_irq_wake() on resume.
- */
-
-/*
- * I2C is used to talk to the MSP430, but this platform device is
- * exposed by an MFD driver that manages I2C communications.
- */
-static struct platform_driver dm355evm_keys_driver = {
- .probe = dm355evm_keys_probe,
- .driver = {
- .name = "dm355evm_keys",
- },
-};
-module_platform_driver(dm355evm_keys_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 17eb84ab4c0b..96cd6a078c8a 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DRV260X haptics driver family
*
* Author: Dan Murphy <dmurphy@ti.com>
*
* Copyright: (C) 2014 Texas Instruments, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/i2c.h>
@@ -157,7 +149,7 @@
/* Control 3 Register */
#define DRV260X_LRA_OPEN_LOOP (1 << 0)
-#define DRV260X_ANANLOG_IN (1 << 1)
+#define DRV260X_ANALOG_IN (1 << 1)
#define DRV260X_LRA_DRV_MODE (1 << 2)
#define DRV260X_RTP_UNSIGNED_DATA (1 << 3)
#define DRV260X_SUPPLY_COMP_DIS (1 << 4)
@@ -175,17 +167,17 @@
/**
* struct drv260x_data -
- * @input_dev - Pointer to the input device
- * @client - Pointer to the I2C client
- * @regmap - Register map of the device
- * @work - Work item used to off load the enable/disable of the vibration
- * @enable_gpio - Pointer to the gpio used for enable/disabling
- * @regulator - Pointer to the regulator for the IC
- * @magnitude - Magnitude of the vibration event
- * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
- * @library - The vibration library to be used
- * @rated_voltage - The rated_voltage of the actuator
- * @overdriver_voltage - The over drive voltage of the actuator
+ * @input_dev: Pointer to the input device
+ * @client: Pointer to the I2C client
+ * @regmap: Register map of the device
+ * @work: Work item used to off load the enable/disable of the vibration
+ * @enable_gpio: Pointer to the gpio used for enable/disabling
+ * @regulator: Pointer to the regulator for the IC
+ * @magnitude: Magnitude of the vibration event
+ * @mode: The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
+ * @library: The vibration library to be used
+ * @rated_voltage: The rated_voltage of the actuator
+ * @overdrive_voltage: The over drive voltage of the actuator
**/
struct drv260x_data {
struct input_dev *input_dev;
@@ -194,60 +186,22 @@ struct drv260x_data {
struct work_struct work;
struct gpio_desc *enable_gpio;
struct regulator *regulator;
- u32 magnitude;
+ u8 magnitude;
u32 mode;
u32 library;
int rated_voltage;
int overdrive_voltage;
};
-static const struct reg_default drv260x_reg_defs[] = {
- { DRV260X_STATUS, 0xe0 },
- { DRV260X_MODE, 0x40 },
- { DRV260X_RT_PB_IN, 0x00 },
- { DRV260X_LIB_SEL, 0x00 },
- { DRV260X_WV_SEQ_1, 0x01 },
- { DRV260X_WV_SEQ_2, 0x00 },
- { DRV260X_WV_SEQ_3, 0x00 },
- { DRV260X_WV_SEQ_4, 0x00 },
- { DRV260X_WV_SEQ_5, 0x00 },
- { DRV260X_WV_SEQ_6, 0x00 },
- { DRV260X_WV_SEQ_7, 0x00 },
- { DRV260X_WV_SEQ_8, 0x00 },
- { DRV260X_GO, 0x00 },
- { DRV260X_OVERDRIVE_OFF, 0x00 },
- { DRV260X_SUSTAIN_P_OFF, 0x00 },
- { DRV260X_SUSTAIN_N_OFF, 0x00 },
- { DRV260X_BRAKE_OFF, 0x00 },
- { DRV260X_A_TO_V_CTRL, 0x05 },
- { DRV260X_A_TO_V_MIN_INPUT, 0x19 },
- { DRV260X_A_TO_V_MAX_INPUT, 0xff },
- { DRV260X_A_TO_V_MIN_OUT, 0x19 },
- { DRV260X_A_TO_V_MAX_OUT, 0xff },
- { DRV260X_RATED_VOLT, 0x3e },
- { DRV260X_OD_CLAMP_VOLT, 0x8c },
- { DRV260X_CAL_COMP, 0x0c },
- { DRV260X_CAL_BACK_EMF, 0x6c },
- { DRV260X_FEEDBACK_CTRL, 0x36 },
- { DRV260X_CTRL1, 0x93 },
- { DRV260X_CTRL2, 0xfa },
- { DRV260X_CTRL3, 0xa0 },
- { DRV260X_CTRL4, 0x20 },
- { DRV260X_CTRL5, 0x80 },
- { DRV260X_LRA_LOOP_PERIOD, 0x33 },
- { DRV260X_VBAT_MON, 0x00 },
- { DRV260X_LRA_RES_PERIOD, 0x00 },
-};
-
#define DRV260X_DEF_RATED_VOLT 0x90
#define DRV260X_DEF_OD_CLAMP_VOLT 0x90
-/**
+/*
* Rated and Overdriver Voltages:
* Calculated using the formula r = v * 255 / 5.6
* where r is what will be written to the register
* and v is the rated or overdriver voltage of the actuator
- **/
+ */
static int drv260x_calculate_voltage(unsigned int voltage)
{
return (voltage * 255 / 5600);
@@ -283,10 +237,11 @@ static int drv260x_haptics_play(struct input_dev *input, void *data,
haptics->mode = DRV260X_LRA_NO_CAL_MODE;
+ /* Scale u16 magnitude into u8 register value */
if (effect->u.rumble.strong_magnitude > 0)
- haptics->magnitude = effect->u.rumble.strong_magnitude;
+ haptics->magnitude = effect->u.rumble.strong_magnitude >> 8;
else if (effect->u.rumble.weak_magnitude > 0)
- haptics->magnitude = effect->u.rumble.weak_magnitude;
+ haptics->magnitude = effect->u.rumble.weak_magnitude >> 8;
else
haptics->magnitude = 0;
@@ -312,7 +267,7 @@ static void drv260x_close(struct input_dev *input)
static const struct reg_sequence drv260x_lra_cal_regs[] = {
{ DRV260X_MODE, DRV260X_AUTO_CAL },
- { DRV260X_CTRL3, DRV260X_NG_THRESH_2 },
+ { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA },
{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
};
@@ -330,7 +285,7 @@ static const struct reg_sequence drv260x_lra_init_regs[] = {
DRV260X_BEMF_GAIN_3 },
{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
- { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
+ { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA | DRV260X_ANALOG_IN },
{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
};
@@ -345,7 +300,7 @@ static const struct reg_sequence drv260x_erm_cal_regs[] = {
{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 |
DRV260X_IDISS_TIME_75 },
- { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
+ { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA },
{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
};
@@ -443,6 +398,7 @@ static int drv260x_init(struct drv260x_data *haptics)
}
do {
+ usleep_range(15000, 15500);
error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
if (error) {
dev_err(&haptics->client->dev,
@@ -460,13 +416,10 @@ static const struct regmap_config drv260x_regmap_config = {
.val_bits = 8,
.max_register = DRV260X_MAX_REG,
- .reg_defaults = drv260x_reg_defs,
- .num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
.cache_type = REGCACHE_NONE,
};
-static int drv260x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int drv260x_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct drv260x_data *haptics;
@@ -581,73 +534,71 @@ static int drv260x_probe(struct i2c_client *client,
return 0;
}
-static int __maybe_unused drv260x_suspend(struct device *dev)
+static int drv260x_suspend(struct device *dev)
{
struct drv260x_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regmap_update_bits(haptics->regmap,
- DRV260X_MODE,
- DRV260X_STANDBY_MASK,
- DRV260X_STANDBY);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regmap_update_bits(haptics->regmap,
+ DRV260X_MODE,
+ DRV260X_STANDBY_MASK,
+ DRV260X_STANDBY);
+ if (error) {
dev_err(dev, "Failed to set standby mode\n");
- goto out;
+ return error;
}
gpiod_set_value(haptics->enable_gpio, 0);
- ret = regulator_disable(haptics->regulator);
- if (ret) {
+ error = regulator_disable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to disable regulator\n");
regmap_update_bits(haptics->regmap,
DRV260X_MODE,
DRV260X_STANDBY_MASK, 0);
+ return error;
}
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+
+ return 0;
}
-static int __maybe_unused drv260x_resume(struct device *dev)
+static int drv260x_resume(struct device *dev)
{
struct drv260x_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regulator_enable(haptics->regulator);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regulator_enable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to enable regulator\n");
- goto out;
+ return error;
}
- ret = regmap_update_bits(haptics->regmap,
- DRV260X_MODE,
- DRV260X_STANDBY_MASK, 0);
- if (ret) {
+ error = regmap_update_bits(haptics->regmap,
+ DRV260X_MODE,
+ DRV260X_STANDBY_MASK, 0);
+ if (error) {
dev_err(dev, "Failed to unset standby mode\n");
regulator_disable(haptics->regulator);
- goto out;
+ return error;
}
gpiod_set_value(haptics->enable_gpio, 1);
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+ return 0;
}
-static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
static const struct i2c_device_id drv260x_id[] = {
- { "drv2605l", 0 },
+ { "drv2605l" },
{ }
};
MODULE_DEVICE_TABLE(i2c, drv260x_id);
@@ -666,7 +617,7 @@ static struct i2c_driver drv260x_driver = {
.driver = {
.name = "drv260x-haptics",
.of_match_table = drv260x_of_match,
- .pm = &drv260x_pm_ops,
+ .pm = pm_sleep_ptr(&drv260x_pm_ops),
},
.id_table = drv260x_id,
};
diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c
index dcb6d8e94b11..46842cb8ddd7 100644
--- a/drivers/input/misc/drv2665.c
+++ b/drivers/input/misc/drv2665.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DRV2665 haptics driver family
*
* Author: Dan Murphy <dmurphy@ti.com>
*
* Copyright: (C) 2015 Texas Instruments, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/i2c.h>
@@ -23,7 +15,7 @@
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
-/* Contol registers */
+/* Control registers */
#define DRV2665_STATUS 0x00
#define DRV2665_CTRL_1 0x01
#define DRV2665_CTRL_2 0x02
@@ -52,11 +44,11 @@
/**
* struct drv2665_data -
- * @input_dev - Pointer to the input device
- * @client - Pointer to the I2C client
- * @regmap - Register map of the device
- * @work - Work item used to off load the enable/disable of the vibration
- * @regulator - Pointer to the regulator for the IC
+ * @input_dev: Pointer to the input device
+ * @client: Pointer to the I2C client
+ * @regmap: Register map of the device
+ * @work: Work item used to off load the enable/disable of the vibration
+ * @regulator: Pointer to the regulator for the IC
*/
struct drv2665_data {
struct input_dev *input_dev;
@@ -164,8 +156,7 @@ static const struct regmap_config drv2665_regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static int drv2665_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int drv2665_probe(struct i2c_client *client)
{
struct drv2665_data *haptics;
int error;
@@ -231,68 +222,66 @@ static int drv2665_probe(struct i2c_client *client,
return 0;
}
-static int __maybe_unused drv2665_suspend(struct device *dev)
+static int drv2665_suspend(struct device *dev)
{
struct drv2665_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
- DRV2665_STANDBY, DRV2665_STANDBY);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+ DRV2665_STANDBY, DRV2665_STANDBY);
+ if (error) {
dev_err(dev, "Failed to set standby mode\n");
regulator_disable(haptics->regulator);
- goto out;
+ return error;
}
- ret = regulator_disable(haptics->regulator);
- if (ret) {
+ error = regulator_disable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to disable regulator\n");
regmap_update_bits(haptics->regmap,
DRV2665_CTRL_2,
DRV2665_STANDBY, 0);
+ return error;
}
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+
+ return 0;
}
-static int __maybe_unused drv2665_resume(struct device *dev)
+static int drv2665_resume(struct device *dev)
{
struct drv2665_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regulator_enable(haptics->regulator);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regulator_enable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to enable regulator\n");
- goto out;
+ return error;
}
- ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
- DRV2665_STANDBY, 0);
- if (ret) {
+ error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+ DRV2665_STANDBY, 0);
+ if (error) {
dev_err(dev, "Failed to unset standby mode\n");
regulator_disable(haptics->regulator);
- goto out;
+ return error;
}
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+ return 0;
}
-static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
static const struct i2c_device_id drv2665_id[] = {
- { "drv2665", 0 },
+ { "drv2665" },
{ }
};
MODULE_DEVICE_TABLE(i2c, drv2665_id);
@@ -310,7 +299,7 @@ static struct i2c_driver drv2665_driver = {
.driver = {
.name = "drv2665-haptics",
.of_match_table = of_match_ptr(drv2665_of_match),
- .pm = &drv2665_pm_ops,
+ .pm = pm_sleep_ptr(&drv2665_pm_ops),
},
.id_table = drv2665_id,
};
diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c
index 6091f0490e8f..f952a24ec595 100644
--- a/drivers/input/misc/drv2667.c
+++ b/drivers/input/misc/drv2667.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DRV2667 haptics driver family
*
* Author: Dan Murphy <dmurphy@ti.com>
*
* Copyright: (C) 2014 Texas Instruments, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/i2c.h>
@@ -24,7 +16,7 @@
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
-/* Contol registers */
+/* Control registers */
#define DRV2667_STATUS 0x00
#define DRV2667_CTRL_1 0x01
#define DRV2667_CTRL_2 0x02
@@ -98,12 +90,14 @@
/**
* struct drv2667_data -
- * @input_dev - Pointer to the input device
- * @client - Pointer to the I2C client
- * @regmap - Register map of the device
- * @work - Work item used to off load the enable/disable of the vibration
- * @regulator - Pointer to the regulator for the IC
- * @magnitude - Magnitude of the vibration event
+ * @input_dev: Pointer to the input device
+ * @client: Pointer to the I2C client
+ * @regmap: Register map of the device
+ * @work: Work item used to off load the enable/disable of the vibration
+ * @regulator: Pointer to the regulator for the IC
+ * @page: Page number
+ * @magnitude: Magnitude of the vibration event
+ * @frequency: Frequency of the vibration event
**/
struct drv2667_data {
struct input_dev *input_dev;
@@ -339,8 +333,7 @@ static const struct regmap_config drv2667_regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static int drv2667_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int drv2667_probe(struct i2c_client *client)
{
struct drv2667_data *haptics;
int error;
@@ -406,68 +399,66 @@ static int drv2667_probe(struct i2c_client *client,
return 0;
}
-static int __maybe_unused drv2667_suspend(struct device *dev)
+static int drv2667_suspend(struct device *dev)
{
struct drv2667_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
- DRV2667_STANDBY, DRV2667_STANDBY);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+ DRV2667_STANDBY, DRV2667_STANDBY);
+ if (error) {
dev_err(dev, "Failed to set standby mode\n");
regulator_disable(haptics->regulator);
- goto out;
+ return error;
}
- ret = regulator_disable(haptics->regulator);
- if (ret) {
+ error = regulator_disable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to disable regulator\n");
regmap_update_bits(haptics->regmap,
DRV2667_CTRL_2,
DRV2667_STANDBY, 0);
+ return error;
}
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+
+ return 0;
}
-static int __maybe_unused drv2667_resume(struct device *dev)
+static int drv2667_resume(struct device *dev)
{
struct drv2667_data *haptics = dev_get_drvdata(dev);
- int ret = 0;
+ int error;
- mutex_lock(&haptics->input_dev->mutex);
+ guard(mutex)(&haptics->input_dev->mutex);
- if (haptics->input_dev->users) {
- ret = regulator_enable(haptics->regulator);
- if (ret) {
+ if (input_device_enabled(haptics->input_dev)) {
+ error = regulator_enable(haptics->regulator);
+ if (error) {
dev_err(dev, "Failed to enable regulator\n");
- goto out;
+ return error;
}
- ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
- DRV2667_STANDBY, 0);
- if (ret) {
+ error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+ DRV2667_STANDBY, 0);
+ if (error) {
dev_err(dev, "Failed to unset standby mode\n");
regulator_disable(haptics->regulator);
- goto out;
+ return error;
}
}
-out:
- mutex_unlock(&haptics->input_dev->mutex);
- return ret;
+ return 0;
}
-static SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume);
static const struct i2c_device_id drv2667_id[] = {
- { "drv2667", 0 },
+ { "drv2667" },
{ }
};
MODULE_DEVICE_TABLE(i2c, drv2667_id);
@@ -485,7 +476,7 @@ static struct i2c_driver drv2667_driver = {
.driver = {
.name = "drv2667-haptics",
.of_match_table = of_match_ptr(drv2667_of_match),
- .pm = &drv2667_pm_ops,
+ .pm = pm_sleep_ptr(&drv2667_pm_ops),
},
.id_table = drv2667_id,
};
diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c
index e956cf1d273f..5bd527117470 100644
--- a/drivers/input/misc/e3x0-button.c
+++ b/drivers/input/misc/e3x0-button.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014, National Instruments Corp. All rights reserved.
*
* Driver for NI Ettus Research USRP E3x0 Button Driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/device.h>
@@ -43,7 +35,7 @@ static irqreturn_t e3x0_button_press_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static int __maybe_unused e3x0_button_suspend(struct device *dev)
+static int e3x0_button_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -53,7 +45,7 @@ static int __maybe_unused e3x0_button_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused e3x0_button_resume(struct device *dev)
+static int e3x0_button_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -63,8 +55,8 @@ static int __maybe_unused e3x0_button_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
- e3x0_button_suspend, e3x0_button_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
+ e3x0_button_suspend, e3x0_button_resume);
static int e3x0_button_probe(struct platform_device *pdev)
{
@@ -73,18 +65,12 @@ static int e3x0_button_probe(struct platform_device *pdev)
int error;
irq_press = platform_get_irq_byname(pdev, "press");
- if (irq_press < 0) {
- dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n",
- irq_press);
+ if (irq_press < 0)
return irq_press;
- }
irq_release = platform_get_irq_byname(pdev, "release");
- if (irq_release < 0) {
- dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n",
- irq_release);
+ if (irq_release < 0)
return irq_release;
- }
input = devm_input_allocate_device(&pdev->dev);
if (!input)
@@ -136,7 +122,7 @@ static struct platform_driver e3x0_button_driver = {
.driver = {
.name = "e3x0-button",
.of_match_table = of_match_ptr(e3x0_button_match),
- .pm = &e3x0_button_pm_ops,
+ .pm = pm_sleep_ptr(&e3x0_button_pm_ops),
},
.probe = e3x0_button_probe,
};
diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c
deleted file mode 100644
index c6a29e57b5e4..000000000000
--- a/drivers/input/misc/gp2ap002a00f.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc.
- *
- * Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
- * Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- */
-
-#include <linux/i2c.h>
-#include <linux/irq.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/input/gp2ap002a00f.h>
-
-struct gp2a_data {
- struct input_dev *input;
- const struct gp2a_platform_data *pdata;
- struct i2c_client *i2c_client;
-};
-
-enum gp2a_addr {
- GP2A_ADDR_PROX = 0x0,
- GP2A_ADDR_GAIN = 0x1,
- GP2A_ADDR_HYS = 0x2,
- GP2A_ADDR_CYCLE = 0x3,
- GP2A_ADDR_OPMOD = 0x4,
- GP2A_ADDR_CON = 0x6
-};
-
-enum gp2a_controls {
- /* Software Shutdown control: 0 = shutdown, 1 = normal operation */
- GP2A_CTRL_SSD = 0x01
-};
-
-static int gp2a_report(struct gp2a_data *dt)
-{
- int vo = gpio_get_value(dt->pdata->vout_gpio);
-
- input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo);
- input_sync(dt->input);
-
- return 0;
-}
-
-static irqreturn_t gp2a_irq(int irq, void *handle)
-{
- struct gp2a_data *dt = handle;
-
- gp2a_report(dt);
-
- return IRQ_HANDLED;
-}
-
-static int gp2a_enable(struct gp2a_data *dt)
-{
- return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
- GP2A_CTRL_SSD);
-}
-
-static int gp2a_disable(struct gp2a_data *dt)
-{
- return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
- 0x00);
-}
-
-static int gp2a_device_open(struct input_dev *dev)
-{
- struct gp2a_data *dt = input_get_drvdata(dev);
- int error;
-
- error = gp2a_enable(dt);
- if (error < 0) {
- dev_err(&dt->i2c_client->dev,
- "unable to activate, err %d\n", error);
- return error;
- }
-
- gp2a_report(dt);
-
- return 0;
-}
-
-static void gp2a_device_close(struct input_dev *dev)
-{
- struct gp2a_data *dt = input_get_drvdata(dev);
- int error;
-
- error = gp2a_disable(dt);
- if (error < 0)
- dev_err(&dt->i2c_client->dev,
- "unable to deactivate, err %d\n", error);
-}
-
-static int gp2a_initialize(struct gp2a_data *dt)
-{
- int error;
-
- error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN,
- 0x08);
- if (error < 0)
- return error;
-
- error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS,
- 0xc2);
- if (error < 0)
- return error;
-
- error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE,
- 0x04);
- if (error < 0)
- return error;
-
- error = gp2a_disable(dt);
-
- return error;
-}
-
-static int gp2a_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- const struct gp2a_platform_data *pdata = dev_get_platdata(&client->dev);
- struct gp2a_data *dt;
- int error;
-
- if (!pdata)
- return -EINVAL;
-
- if (pdata->hw_setup) {
- error = pdata->hw_setup(client);
- if (error < 0)
- return error;
- }
-
- error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME);
- if (error)
- goto err_hw_shutdown;
-
- dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL);
- if (!dt) {
- error = -ENOMEM;
- goto err_free_gpio;
- }
-
- dt->pdata = pdata;
- dt->i2c_client = client;
-
- error = gp2a_initialize(dt);
- if (error < 0)
- goto err_free_mem;
-
- dt->input = input_allocate_device();
- if (!dt->input) {
- error = -ENOMEM;
- goto err_free_mem;
- }
-
- input_set_drvdata(dt->input, dt);
-
- dt->input->open = gp2a_device_open;
- dt->input->close = gp2a_device_close;
- dt->input->name = GP2A_I2C_NAME;
- dt->input->id.bustype = BUS_I2C;
- dt->input->dev.parent = &client->dev;
-
- input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY);
-
- error = request_threaded_irq(client->irq, NULL, gp2a_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- GP2A_I2C_NAME, dt);
- if (error) {
- dev_err(&client->dev, "irq request failed\n");
- goto err_free_input_dev;
- }
-
- error = input_register_device(dt->input);
- if (error) {
- dev_err(&client->dev, "device registration failed\n");
- goto err_free_irq;
- }
-
- device_init_wakeup(&client->dev, pdata->wakeup);
- i2c_set_clientdata(client, dt);
-
- return 0;
-
-err_free_irq:
- free_irq(client->irq, dt);
-err_free_input_dev:
- input_free_device(dt->input);
-err_free_mem:
- kfree(dt);
-err_free_gpio:
- gpio_free(pdata->vout_gpio);
-err_hw_shutdown:
- if (pdata->hw_shutdown)
- pdata->hw_shutdown(client);
- return error;
-}
-
-static int gp2a_remove(struct i2c_client *client)
-{
- struct gp2a_data *dt = i2c_get_clientdata(client);
- const struct gp2a_platform_data *pdata = dt->pdata;
-
- free_irq(client->irq, dt);
-
- input_unregister_device(dt->input);
- kfree(dt);
-
- gpio_free(pdata->vout_gpio);
-
- if (pdata->hw_shutdown)
- pdata->hw_shutdown(client);
-
- return 0;
-}
-
-static int __maybe_unused gp2a_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct gp2a_data *dt = i2c_get_clientdata(client);
- int retval = 0;
-
- if (device_may_wakeup(&client->dev)) {
- enable_irq_wake(client->irq);
- } else {
- mutex_lock(&dt->input->mutex);
- if (dt->input->users)
- retval = gp2a_disable(dt);
- mutex_unlock(&dt->input->mutex);
- }
-
- return retval;
-}
-
-static int __maybe_unused gp2a_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct gp2a_data *dt = i2c_get_clientdata(client);
- int retval = 0;
-
- if (device_may_wakeup(&client->dev)) {
- disable_irq_wake(client->irq);
- } else {
- mutex_lock(&dt->input->mutex);
- if (dt->input->users)
- retval = gp2a_enable(dt);
- mutex_unlock(&dt->input->mutex);
- }
-
- return retval;
-}
-
-static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume);
-
-static const struct i2c_device_id gp2a_i2c_id[] = {
- { GP2A_I2C_NAME, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, gp2a_i2c_id);
-
-static struct i2c_driver gp2a_i2c_driver = {
- .driver = {
- .name = GP2A_I2C_NAME,
- .pm = &gp2a_pm,
- },
- .probe = gp2a_probe,
- .remove = gp2a_remove,
- .id_table = gp2a_i2c_id,
-};
-
-module_i2c_driver(gp2a_i2c_driver);
-
-MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>");
-MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c
index 16272fffeb7e..3d65cb4f4ef3 100644
--- a/drivers/input/misc/gpio-beeper.c
+++ b/drivers/input/misc/gpio-beeper.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic GPIO beeper driver
*
* Copyright (C) 2013-2014 Alexander Shiyan <shc_work@mail.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/input.h>
@@ -98,7 +94,7 @@ static int gpio_beeper_probe(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id gpio_beeper_of_match[] = {
- { .compatible = BEEPER_MODNAME, },
+ { .compatible = "gpio-beeper", },
{ }
};
MODULE_DEVICE_TABLE(of, gpio_beeper_of_match);
diff --git a/drivers/input/misc/gpio-vibra.c b/drivers/input/misc/gpio-vibra.c
new file mode 100644
index 000000000000..ad44b4d18a2a
--- /dev/null
+++ b/drivers/input/misc/gpio-vibra.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPIO vibrator driver
+ *
+ * Copyright (C) 2019 Luca Weiss <luca@z3ntu.xyz>
+ *
+ * Based on PWM vibrator driver:
+ * Copyright (C) 2017 Collabora Ltd.
+ *
+ * Based on previous work from:
+ * Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ *
+ * Based on PWM beeper driver:
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+struct gpio_vibrator {
+ struct input_dev *input;
+ struct gpio_desc *gpio;
+ struct regulator *vcc;
+
+ struct work_struct play_work;
+ bool running;
+ bool vcc_on;
+};
+
+static int gpio_vibrator_start(struct gpio_vibrator *vibrator)
+{
+ struct device *pdev = vibrator->input->dev.parent;
+ int err;
+
+ if (!vibrator->vcc_on) {
+ err = regulator_enable(vibrator->vcc);
+ if (err) {
+ dev_err(pdev, "failed to enable regulator: %d\n", err);
+ return err;
+ }
+ vibrator->vcc_on = true;
+ }
+
+ gpiod_set_value_cansleep(vibrator->gpio, 1);
+
+ return 0;
+}
+
+static void gpio_vibrator_stop(struct gpio_vibrator *vibrator)
+{
+ gpiod_set_value_cansleep(vibrator->gpio, 0);
+
+ if (vibrator->vcc_on) {
+ regulator_disable(vibrator->vcc);
+ vibrator->vcc_on = false;
+ }
+}
+
+static void gpio_vibrator_play_work(struct work_struct *work)
+{
+ struct gpio_vibrator *vibrator =
+ container_of(work, struct gpio_vibrator, play_work);
+
+ if (vibrator->running)
+ gpio_vibrator_start(vibrator);
+ else
+ gpio_vibrator_stop(vibrator);
+}
+
+static int gpio_vibrator_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct gpio_vibrator *vibrator = input_get_drvdata(dev);
+ int level;
+
+ level = effect->u.rumble.strong_magnitude;
+ if (!level)
+ level = effect->u.rumble.weak_magnitude;
+
+ vibrator->running = level;
+ schedule_work(&vibrator->play_work);
+
+ return 0;
+}
+
+static void gpio_vibrator_close(struct input_dev *input)
+{
+ struct gpio_vibrator *vibrator = input_get_drvdata(input);
+
+ cancel_work_sync(&vibrator->play_work);
+ gpio_vibrator_stop(vibrator);
+ vibrator->running = false;
+}
+
+static int gpio_vibrator_probe(struct platform_device *pdev)
+{
+ struct gpio_vibrator *vibrator;
+ int err;
+
+ vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+ if (!vibrator)
+ return -ENOMEM;
+
+ vibrator->input = devm_input_allocate_device(&pdev->dev);
+ if (!vibrator->input)
+ return -ENOMEM;
+
+ vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+ if (IS_ERR(vibrator->vcc))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc),
+ "Failed to request regulator\n");
+
+ vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(vibrator->gpio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->gpio),
+ "Failed to request main gpio\n");
+
+ INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work);
+
+ vibrator->input->name = "gpio-vibrator";
+ vibrator->input->id.bustype = BUS_HOST;
+ vibrator->input->close = gpio_vibrator_close;
+
+ input_set_drvdata(vibrator->input, vibrator);
+ input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+ err = input_ff_create_memless(vibrator->input, NULL,
+ gpio_vibrator_play_effect);
+ if (err) {
+ dev_err(&pdev->dev, "Couldn't create FF dev: %d\n", err);
+ return err;
+ }
+
+ err = input_register_device(vibrator->input);
+ if (err) {
+ dev_err(&pdev->dev, "Couldn't register input dev: %d\n", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, vibrator);
+
+ return 0;
+}
+
+static int gpio_vibrator_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&vibrator->play_work);
+ if (vibrator->running)
+ gpio_vibrator_stop(vibrator);
+
+ return 0;
+}
+
+static int gpio_vibrator_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ if (vibrator->running)
+ gpio_vibrator_start(vibrator);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(gpio_vibrator_pm_ops,
+ gpio_vibrator_suspend, gpio_vibrator_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_vibra_dt_match_table[] = {
+ { .compatible = "gpio-vibrator" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
+#endif
+
+static struct platform_driver gpio_vibrator_driver = {
+ .probe = gpio_vibrator_probe,
+ .driver = {
+ .name = "gpio-vibrator",
+ .pm = pm_sleep_ptr(&gpio_vibrator_pm_ops),
+ .of_match_table = of_match_ptr(gpio_vibra_dt_match_table),
+ },
+};
+module_platform_driver(gpio_vibrator_driver);
+
+MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xy>");
+MODULE_DESCRIPTION("GPIO vibrator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-vibrator");
diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c
index 1dca526e6f1a..ee668eba302f 100644
--- a/drivers/input/misc/gpio_decoder.c
+++ b/drivers/input/misc/gpio_decoder.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* A generic driver to read multiple gpio lines and translate the
* encoded numeric value into an input event.
*/
@@ -17,14 +9,12 @@
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
-#include <linux/input-polldev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
struct gpio_decoder {
- struct input_polled_dev *poll_dev;
struct gpio_descs *input_gpios;
struct device *dev;
u32 axis;
@@ -53,15 +43,15 @@ static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder)
return ret;
}
-static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev)
+static void gpio_decoder_poll_gpios(struct input_dev *input)
{
- struct gpio_decoder *decoder = poll_dev->private;
+ struct gpio_decoder *decoder = input_get_drvdata(input);
int state;
state = gpio_decoder_get_gpios_state(decoder);
if (state >= 0 && state != decoder->last_stable) {
- input_report_abs(poll_dev->input, decoder->axis, state);
- input_sync(poll_dev->input);
+ input_report_abs(input, decoder->axis, state);
+ input_sync(input);
decoder->last_stable = state;
}
}
@@ -70,20 +60,23 @@ static int gpio_decoder_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_decoder *decoder;
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
u32 max;
int err;
- decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL);
+ decoder = devm_kzalloc(dev, sizeof(*decoder), GFP_KERNEL);
if (!decoder)
return -ENOMEM;
+ decoder->dev = dev;
device_property_read_u32(dev, "linux,axis", &decoder->axis);
+
decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN);
if (IS_ERR(decoder->input_gpios)) {
dev_err(dev, "unable to acquire input gpios\n");
return PTR_ERR(decoder->input_gpios);
}
+
if (decoder->input_gpios->ndescs < 2) {
dev_err(dev, "not enough gpios found\n");
return -EINVAL;
@@ -92,22 +85,25 @@ static int gpio_decoder_probe(struct platform_device *pdev)
if (device_property_read_u32(dev, "decoder-max-value", &max))
max = (1U << decoder->input_gpios->ndescs) - 1;
- decoder->dev = dev;
- poll_dev = devm_input_allocate_polled_device(decoder->dev);
- if (!poll_dev)
+ input = devm_input_allocate_device(dev);
+ if (!input)
return -ENOMEM;
- poll_dev->private = decoder;
- poll_dev->poll = gpio_decoder_poll_gpios;
- decoder->poll_dev = poll_dev;
+ input_set_drvdata(input, decoder);
+
+ input->name = pdev->name;
+ input->id.bustype = BUS_HOST;
+ input_set_abs_params(input, decoder->axis, 0, max, 0, 0);
- poll_dev->input->name = pdev->name;
- poll_dev->input->id.bustype = BUS_HOST;
- input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0);
+ err = input_setup_polling(input, gpio_decoder_poll_gpios);
+ if (err) {
+ dev_err(dev, "failed to set up polling\n");
+ return err;
+ }
- err = input_register_polled_device(poll_dev);
+ err = input_register_device(input);
if (err) {
- dev_err(dev, "failed to register polled device\n");
+ dev_err(dev, "failed to register input device\n");
return err;
}
diff --git a/drivers/input/misc/hisi_powerkey.c b/drivers/input/misc/hisi_powerkey.c
index dee6245f38d7..d315017324d9 100644
--- a/drivers/input/misc/hisi_powerkey.c
+++ b/drivers/input/misc/hisi_powerkey.c
@@ -30,7 +30,7 @@ static irqreturn_t hi65xx_power_press_isr(int irq, void *q)
{
struct input_dev *input = q;
- pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
+ pm_wakeup_dev_event(input->dev.parent, MAX_HELD_TIME, true);
input_report_key(input, KEY_POWER, 1);
input_sync(input);
@@ -90,12 +90,8 @@ static int hi65xx_powerkey_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) {
irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name);
- if (irq < 0) {
- error = irq;
- dev_err(dev, "couldn't get irq %s: %d\n",
- hi65xx_irq_info[i].name, error);
- return error;
- }
+ if (irq < 0)
+ return irq;
error = devm_request_any_context_irq(dev, irq,
hi65xx_irq_info[i].handler,
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index abca895a6156..afc0d6dc5787 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -53,28 +53,10 @@ MODULE_LICENSE("Dual BSD/GPL");
#define RTC_VERSION "1.10d"
-static DEFINE_MUTEX(hp_sdc_rtc_mutex);
static unsigned long epoch = 2000;
static struct semaphore i8042tregs;
-static hp_sdc_irqhook hp_sdc_rtc_isr;
-
-static struct fasync_struct *hp_sdc_rtc_async_queue;
-
-static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
-
-static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos);
-
-static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg);
-
-static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
-
-static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
-static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
-
static void hp_sdc_rtc_isr (int irq, void *dev_id,
uint8_t status, uint8_t data)
{
@@ -283,152 +265,7 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
return 0;
}
-
-#if 0 /* not used yet */
-/* Set the i8042 real-time clock */
-static int hp_sdc_rtc_set_rt (struct timeval *setto)
-{
- uint32_t tenms;
- unsigned int days;
- hp_sdc_transaction t;
- uint8_t tseq[11] = {
- HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
- HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
- HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
- HP_SDC_CMD_SET_RTD, 2, 0, 0
- };
-
- t.endidx = 10;
-
- if (0xffff < setto->tv_sec / 86400) return -1;
- days = setto->tv_sec / 86400;
- if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
- days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
- if (days > 0xffff) return -1;
-
- if (0xffffff < setto->tv_sec) return -1;
- tenms = setto->tv_sec * 100;
- if (0xffffff < setto->tv_usec / 10000) return -1;
- tenms += setto->tv_usec / 10000;
- if (tenms > 0xffffff) return -1;
-
- tseq[3] = (uint8_t)(tenms & 0xff);
- tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
- tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
-
- tseq[9] = (uint8_t)(days & 0xff);
- tseq[10] = (uint8_t)((days >> 8) & 0xff);
-
- t.seq = tseq;
-
- if (hp_sdc_enqueue_transaction(&t)) return -1;
- return 0;
-}
-
-/* Set the i8042 fast handshake timer */
-static int hp_sdc_rtc_set_fhs (struct timeval *setto)
-{
- uint32_t tenms;
- hp_sdc_transaction t;
- uint8_t tseq[5] = {
- HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
- HP_SDC_CMD_SET_FHS, 2, 0, 0
- };
-
- t.endidx = 4;
-
- if (0xffff < setto->tv_sec) return -1;
- tenms = setto->tv_sec * 100;
- if (0xffff < setto->tv_usec / 10000) return -1;
- tenms += setto->tv_usec / 10000;
- if (tenms > 0xffff) return -1;
-
- tseq[3] = (uint8_t)(tenms & 0xff);
- tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
-
- t.seq = tseq;
-
- if (hp_sdc_enqueue_transaction(&t)) return -1;
- return 0;
-}
-
-
-/* Set the i8042 match timer (a.k.a. alarm) */
-#define hp_sdc_rtc_set_mt (setto) \
- hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
-
-/* Set the i8042 delay timer */
-#define hp_sdc_rtc_set_dt (setto) \
- hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
-
-/* Set the i8042 cycle timer (a.k.a. periodic) */
-#define hp_sdc_rtc_set_ct (setto) \
- hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
-
-/* Set one of the i8042 3-byte wide timers */
-static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
-{
- uint32_t tenms;
- hp_sdc_transaction t;
- uint8_t tseq[6] = {
- HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
- 0, 3, 0, 0, 0
- };
-
- t.endidx = 6;
-
- if (0xffffff < setto->tv_sec) return -1;
- tenms = setto->tv_sec * 100;
- if (0xffffff < setto->tv_usec / 10000) return -1;
- tenms += setto->tv_usec / 10000;
- if (tenms > 0xffffff) return -1;
-
- tseq[1] = setcmd;
- tseq[3] = (uint8_t)(tenms & 0xff);
- tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
- tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
-
- t.seq = tseq;
-
- if (hp_sdc_enqueue_transaction(&t)) {
- return -1;
- }
- return 0;
-}
-#endif
-
-static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos) {
- ssize_t retval;
-
- if (count < sizeof(unsigned long))
- return -EINVAL;
-
- retval = put_user(68, (unsigned long __user *)buf);
- return retval;
-}
-
-static __poll_t hp_sdc_rtc_poll(struct file *file, poll_table *wait)
-{
- unsigned long l;
-
- l = 0;
- if (l != 0)
- return EPOLLIN | EPOLLRDNORM;
- return 0;
-}
-
-static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
-{
- return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
-}
-
-static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
+static int __maybe_unused hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
{
#define YN(bit) ("no")
#define NY(bit) ("yes")
@@ -507,182 +344,6 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
#undef NY
}
-static int hp_sdc_rtc_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
-#if 1
- return -EINVAL;
-#else
-
- struct rtc_time wtime;
- struct timeval ttime;
- int use_wtime = 0;
-
- /* This needs major work. */
-
- switch (cmd) {
-
- case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
- case RTC_AIE_ON: /* Allow alarm interrupts. */
- case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
- case RTC_PIE_ON: /* Allow periodic ints */
- case RTC_UIE_ON: /* Allow ints for RTC updates. */
- case RTC_UIE_OFF: /* Allow ints for RTC updates. */
- {
- /* We cannot mask individual user timers and we
- cannot tell them apart when they occur, so it
- would be disingenuous to succeed these IOCTLs */
- return -EINVAL;
- }
- case RTC_ALM_READ: /* Read the present alarm time */
- {
- if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
- if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
-
- wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600;
- wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60;
- wtime.tm_sec = ttime.tv_sec;
-
- break;
- }
- case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
- {
- return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
- }
- case RTC_IRQP_SET: /* Set periodic IRQ rate. */
- {
- /*
- * The max we can do is 100Hz.
- */
-
- if ((arg < 1) || (arg > 100)) return -EINVAL;
- ttime.tv_sec = 0;
- ttime.tv_usec = 1000000 / arg;
- if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
- hp_sdc_rtc_freq = arg;
- return 0;
- }
- case RTC_ALM_SET: /* Store a time into the alarm */
- {
- /*
- * This expects a struct hp_sdc_rtc_time. Writing 0xff means
- * "don't care" or "match all" for PC timers. The HP SDC
- * does not support that perk, but it could be emulated fairly
- * easily. Only the tm_hour, tm_min and tm_sec are used.
- * We could do it with 10ms accuracy with the HP SDC, if the
- * rtc interface left us a way to do that.
- */
- struct hp_sdc_rtc_time alm_tm;
-
- if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
- sizeof(struct hp_sdc_rtc_time)))
- return -EFAULT;
-
- if (alm_tm.tm_hour > 23) return -EINVAL;
- if (alm_tm.tm_min > 59) return -EINVAL;
- if (alm_tm.tm_sec > 59) return -EINVAL;
-
- ttime.sec = alm_tm.tm_hour * 3600 +
- alm_tm.tm_min * 60 + alm_tm.tm_sec;
- ttime.usec = 0;
- if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
- return 0;
- }
- case RTC_RD_TIME: /* Read the time/date from RTC */
- {
- if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
- break;
- }
- case RTC_SET_TIME: /* Set the RTC */
- {
- struct rtc_time hp_sdc_rtc_tm;
- unsigned char mon, day, hrs, min, sec, leap_yr;
- unsigned int yrs;
-
- if (!capable(CAP_SYS_TIME))
- return -EACCES;
- if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
- sizeof(struct rtc_time)))
- return -EFAULT;
-
- yrs = hp_sdc_rtc_tm.tm_year + 1900;
- mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
- day = hp_sdc_rtc_tm.tm_mday;
- hrs = hp_sdc_rtc_tm.tm_hour;
- min = hp_sdc_rtc_tm.tm_min;
- sec = hp_sdc_rtc_tm.tm_sec;
-
- if (yrs < 1970)
- return -EINVAL;
-
- leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
-
- if ((mon > 12) || (day == 0))
- return -EINVAL;
- if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
- return -EINVAL;
- if ((hrs >= 24) || (min >= 60) || (sec >= 60))
- return -EINVAL;
-
- if ((yrs -= eH) > 255) /* They are unsigned */
- return -EINVAL;
-
-
- return 0;
- }
- case RTC_EPOCH_READ: /* Read the epoch. */
- {
- return put_user (epoch, (unsigned long *)arg);
- }
- case RTC_EPOCH_SET: /* Set the epoch. */
- {
- /*
- * There were no RTC clocks before 1900.
- */
- if (arg < 1900)
- return -EINVAL;
- if (!capable(CAP_SYS_TIME))
- return -EACCES;
-
- epoch = arg;
- return 0;
- }
- default:
- return -EINVAL;
- }
- return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
-#endif
-}
-
-static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&hp_sdc_rtc_mutex);
- ret = hp_sdc_rtc_ioctl(file, cmd, arg);
- mutex_unlock(&hp_sdc_rtc_mutex);
-
- return ret;
-}
-
-
-static const struct file_operations hp_sdc_rtc_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = hp_sdc_rtc_read,
- .poll = hp_sdc_rtc_poll,
- .unlocked_ioctl = hp_sdc_rtc_unlocked_ioctl,
- .open = hp_sdc_rtc_open,
- .fasync = hp_sdc_rtc_fasync,
-};
-
-static struct miscdevice hp_sdc_rtc_dev = {
- .minor = RTC_MINOR,
- .name = "rtc_HIL",
- .fops = &hp_sdc_rtc_fops
-};
-
static int __init hp_sdc_rtc_init(void)
{
int ret;
@@ -696,8 +357,6 @@ static int __init hp_sdc_rtc_init(void)
if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
return ret;
- if (misc_register(&hp_sdc_rtc_dev) != 0)
- printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show);
@@ -710,7 +369,6 @@ static int __init hp_sdc_rtc_init(void)
static void __exit hp_sdc_rtc_exit(void)
{
remove_proc_entry ("driver/rtc", NULL);
- misc_deregister(&hp_sdc_rtc_dev);
hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
}
diff --git a/drivers/input/misc/ibm-panel.c b/drivers/input/misc/ibm-panel.c
new file mode 100644
index 000000000000..aa48f62d7ea0
--- /dev/null
+++ b/drivers/input/misc/ibm-panel.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) IBM Corporation 2020
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+
+#define DEVICE_NAME "ibm-panel"
+#define PANEL_KEYCODES_COUNT 3
+
+struct ibm_panel {
+ u8 idx;
+ u8 command[11];
+ u32 keycodes[PANEL_KEYCODES_COUNT];
+ spinlock_t lock; /* protects writes to idx and command */
+ struct input_dev *input;
+};
+
+static u8 ibm_panel_calculate_checksum(struct ibm_panel *panel)
+{
+ u8 chksum;
+ u16 sum = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(panel->command) - 1; ++i) {
+ sum += panel->command[i];
+ if (sum & 0xff00) {
+ sum &= 0xff;
+ sum++;
+ }
+ }
+
+ chksum = sum & 0xff;
+ chksum = ~chksum;
+ chksum++;
+
+ return chksum;
+}
+
+static void ibm_panel_process_command(struct ibm_panel *panel)
+{
+ u8 button;
+ u8 chksum;
+
+ if (panel->command[0] != 0xff && panel->command[1] != 0xf0) {
+ dev_dbg(&panel->input->dev, "command invalid: %02x %02x\n",
+ panel->command[0], panel->command[1]);
+ return;
+ }
+
+ chksum = ibm_panel_calculate_checksum(panel);
+ if (chksum != panel->command[sizeof(panel->command) - 1]) {
+ dev_dbg(&panel->input->dev,
+ "command failed checksum: %u != %u\n", chksum,
+ panel->command[sizeof(panel->command) - 1]);
+ return;
+ }
+
+ button = panel->command[2] & 0xf;
+ if (button < PANEL_KEYCODES_COUNT) {
+ input_report_key(panel->input, panel->keycodes[button],
+ !(panel->command[2] & 0x80));
+ input_sync(panel->input);
+ } else {
+ dev_dbg(&panel->input->dev, "unknown button %u\n",
+ button);
+ }
+}
+
+static int ibm_panel_i2c_slave_cb(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ struct ibm_panel *panel = i2c_get_clientdata(client);
+
+ dev_dbg(&panel->input->dev, "event: %u data: %02x\n", event, *val);
+
+ guard(spinlock_irqsave)(&panel->lock);
+
+ switch (event) {
+ case I2C_SLAVE_STOP:
+ if (panel->idx == sizeof(panel->command))
+ ibm_panel_process_command(panel);
+ else
+ dev_dbg(&panel->input->dev,
+ "command incorrect size %u\n", panel->idx);
+ fallthrough;
+ case I2C_SLAVE_WRITE_REQUESTED:
+ panel->idx = 0;
+ break;
+ case I2C_SLAVE_WRITE_RECEIVED:
+ if (panel->idx < sizeof(panel->command))
+ panel->command[panel->idx++] = *val;
+ else
+ /*
+ * The command is too long and therefore invalid, so set the index
+ * to it's largest possible value. When a STOP is finally received,
+ * the command will be rejected upon processing.
+ */
+ panel->idx = U8_MAX;
+ break;
+ case I2C_SLAVE_READ_REQUESTED:
+ case I2C_SLAVE_READ_PROCESSED:
+ *val = 0xff;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ibm_panel_probe(struct i2c_client *client)
+{
+ struct ibm_panel *panel;
+ int i;
+ int error;
+
+ panel = devm_kzalloc(&client->dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+
+ spin_lock_init(&panel->lock);
+
+ panel->input = devm_input_allocate_device(&client->dev);
+ if (!panel->input)
+ return -ENOMEM;
+
+ panel->input->name = client->name;
+ panel->input->id.bustype = BUS_I2C;
+
+ error = device_property_read_u32_array(&client->dev,
+ "linux,keycodes",
+ panel->keycodes,
+ PANEL_KEYCODES_COUNT);
+ if (error) {
+ /*
+ * Use gamepad buttons as defaults for compatibility with
+ * existing applications.
+ */
+ panel->keycodes[0] = BTN_NORTH;
+ panel->keycodes[1] = BTN_SOUTH;
+ panel->keycodes[2] = BTN_SELECT;
+ }
+
+ for (i = 0; i < PANEL_KEYCODES_COUNT; ++i)
+ input_set_capability(panel->input, EV_KEY, panel->keycodes[i]);
+
+ error = input_register_device(panel->input);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ i2c_set_clientdata(client, panel);
+ error = i2c_slave_register(client, ibm_panel_i2c_slave_cb);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register as i2c slave: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void ibm_panel_remove(struct i2c_client *client)
+{
+ i2c_slave_unregister(client);
+}
+
+static const struct of_device_id ibm_panel_match[] = {
+ { .compatible = "ibm,op-panel" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ibm_panel_match);
+
+static struct i2c_driver ibm_panel_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = ibm_panel_match,
+ },
+ .probe = ibm_panel_probe,
+ .remove = ibm_panel_remove,
+};
+module_i2c_driver(ibm_panel_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("IBM Operation Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c
index b0acb878d1cf..ab2e0a401904 100644
--- a/drivers/input/misc/ideapad_slidebar.c
+++ b/drivers/input/misc/ideapad_slidebar.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Input driver for slidebars on some Lenovo IdeaPad laptops
*
@@ -5,11 +6,6 @@
*
* Reverse-engineered from Lenovo SlideNav software (SBarHook.dll).
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
* Trademarks are the property of their respective owners.
*/
@@ -27,7 +23,7 @@
*
* The value is in byte range, however, I only figured out
* how bits 0b10011001 work. Some other bits, probably,
- * are meaningfull too.
+ * are meaningful too.
*
* Possible states:
*
@@ -99,45 +95,33 @@ static struct platform_device *slidebar_platform_dev;
static u8 slidebar_pos_get(void)
{
- u8 res;
- unsigned long flags;
+ guard(spinlock_irqsave)(&io_lock);
- spin_lock_irqsave(&io_lock, flags);
outb(0xf4, 0xff29);
outb(0xbf, 0xff2a);
- res = inb(0xff2b);
- spin_unlock_irqrestore(&io_lock, flags);
-
- return res;
+ return inb(0xff2b);
}
static u8 slidebar_mode_get(void)
{
- u8 res;
- unsigned long flags;
+ guard(spinlock_irqsave)(&io_lock);
- spin_lock_irqsave(&io_lock, flags);
outb(0xf7, 0xff29);
outb(0x8b, 0xff2a);
- res = inb(0xff2b);
- spin_unlock_irqrestore(&io_lock, flags);
-
- return res;
+ return inb(0xff2b);
}
static void slidebar_mode_set(u8 mode)
{
- unsigned long flags;
+ guard(spinlock_irqsave)(&io_lock);
- spin_lock_irqsave(&io_lock, flags);
outb(0xf7, 0xff29);
outb(0x8b, 0xff2a);
outb(mode, 0xff2b);
- spin_unlock_irqrestore(&io_lock, flags);
}
static bool slidebar_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+ struct serio *port, void *context)
{
static bool extended = false;
@@ -235,7 +219,7 @@ static int __init ideapad_probe(struct platform_device* pdev)
input_set_capability(slidebar_input_dev, EV_ABS, ABS_X);
input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0);
- err = i8042_install_filter(slidebar_i8042_filter);
+ err = i8042_install_filter(slidebar_i8042_filter, NULL);
if (err) {
dev_err(&pdev->dev,
"Failed to install i8042 filter: %d\n", err);
@@ -260,13 +244,11 @@ err_release_ports:
return err;
}
-static int ideapad_remove(struct platform_device *pdev)
+static void ideapad_remove(struct platform_device *pdev)
{
i8042_remove_filter(slidebar_i8042_filter);
input_unregister_device(slidebar_input_dev);
release_region(IDEAPAD_BASE, 3);
-
- return 0;
}
static struct platform_driver slidebar_drv = {
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 3d51175c4d72..4581f1c53644 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for IMS Passenger Control Unit Devices
*
* Copyright (C) 2013 The IMS Company
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
*/
#include <linux/completion.h>
@@ -20,7 +17,7 @@
#include <linux/types.h>
#include <linux/usb/input.h>
#include <linux/usb/cdc.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define IMS_PCU_KEYMAP_LEN 32
@@ -39,16 +36,14 @@ struct ims_pcu_gamepad {
struct ims_pcu_backlight {
struct led_classdev cdev;
- struct work_struct work;
- enum led_brightness desired_brightness;
char name[32];
};
#define IMS_PCU_PART_NUMBER_LEN 15
#define IMS_PCU_SERIAL_NUMBER_LEN 8
#define IMS_PCU_DOM_LEN 8
-#define IMS_PCU_FW_VERSION_LEN (9 + 1)
-#define IMS_PCU_BL_VERSION_LEN (9 + 1)
+#define IMS_PCU_FW_VERSION_LEN 16
+#define IMS_PCU_BL_VERSION_LEN 16
#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
#define IMS_PCU_PCU_B_DEVICE_ID 5
@@ -212,8 +207,7 @@ static int ims_pcu_setup_buttons(struct ims_pcu *pcu,
input = input_allocate_device();
if (!input) {
- dev_err(pcu->dev,
- "Not enough memory for input input device\n");
+ dev_err(pcu->dev, "Not enough memory for input device\n");
return -ENOMEM;
}
@@ -292,7 +286,7 @@ static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
struct input_dev *input;
int error;
- gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL);
+ gamepad = kzalloc(sizeof(*gamepad), GFP_KERNEL);
input = input_allocate_device();
if (!gamepad || !input) {
dev_err(pcu->dev,
@@ -340,7 +334,7 @@ static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
err_free_mem:
input_free_device(input);
kfree(gamepad);
- return -ENOMEM;
+ return error;
}
static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
@@ -652,8 +646,8 @@ static int __ims_pcu_execute_command(struct ims_pcu *pcu,
#define IMS_PCU_BL_DATA_OFFSET 3
static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
- u8 command, const void *data, size_t len,
- u8 expected_response, int response_time)
+ u8 command, const void *data, size_t len,
+ u8 expected_response, int response_time)
{
int error;
@@ -745,11 +739,11 @@ static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
{
int error;
- /* Execute jump to the bootoloader */
+ /* Execute jump to the bootloader */
error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
if (error) {
dev_err(pcu->dev,
- "Failure when sending JUMP TO BOOLTLOADER command, error: %d\n",
+ "Failure when sending JUMP TO BOOTLOADER command, error: %d\n",
error);
return error;
}
@@ -766,7 +760,7 @@ static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
struct ims_pcu_flash_fmt {
__le32 addr;
u8 len;
- u8 data[];
+ u8 data[] __counted_by(len);
};
static unsigned int ims_pcu_count_fw_records(const struct firmware *fw)
@@ -850,6 +844,12 @@ static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
addr = be32_to_cpu(rec->addr) / 2;
len = be16_to_cpu(rec->len);
+ if (len > sizeof(pcu->cmd_buf) - 1 - sizeof(*fragment)) {
+ dev_err(pcu->dev,
+ "Invalid record length in firmware: %d\n", len);
+ return -EINVAL;
+ }
+
fragment = (void *)&pcu->cmd_buf[1];
put_unaligned_le32(addr, &fragment->addr);
fragment->len = len;
@@ -933,9 +933,8 @@ static void ims_pcu_process_async_firmware(const struct firmware *fw,
goto out;
}
- mutex_lock(&pcu->cmd_mutex);
- ims_pcu_handle_firmware_update(pcu, fw);
- mutex_unlock(&pcu->cmd_mutex);
+ scoped_guard(mutex, &pcu->cmd_mutex)
+ ims_pcu_handle_firmware_update(pcu, fw);
release_firmware(fw);
@@ -949,36 +948,26 @@ out:
#define IMS_PCU_MAX_BRIGHTNESS 31998
-static void ims_pcu_backlight_work(struct work_struct *work)
+static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+ enum led_brightness value)
{
struct ims_pcu_backlight *backlight =
- container_of(work, struct ims_pcu_backlight, work);
+ container_of(cdev, struct ims_pcu_backlight, cdev);
struct ims_pcu *pcu =
container_of(backlight, struct ims_pcu, backlight);
- int desired_brightness = backlight->desired_brightness;
- __le16 br_val = cpu_to_le16(desired_brightness);
+ __le16 br_val = cpu_to_le16(value);
int error;
- mutex_lock(&pcu->cmd_mutex);
+ guard(mutex)(&pcu->cmd_mutex);
error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS,
&br_val, sizeof(br_val));
if (error && error != -ENODEV)
dev_warn(pcu->dev,
"Failed to set desired brightness %u, error: %d\n",
- desired_brightness, error);
-
- mutex_unlock(&pcu->cmd_mutex);
-}
-
-static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct ims_pcu_backlight *backlight =
- container_of(cdev, struct ims_pcu_backlight, cdev);
+ value, error);
- backlight->desired_brightness = value;
- schedule_work(&backlight->work);
+ return error;
}
static enum led_brightness
@@ -991,7 +980,7 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
int brightness;
int error;
- mutex_lock(&pcu->cmd_mutex);
+ guard(mutex)(&pcu->cmd_mutex);
error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS);
if (error) {
@@ -1005,8 +994,6 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
}
- mutex_unlock(&pcu->cmd_mutex);
-
return brightness;
}
@@ -1015,14 +1002,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
int error;
- INIT_WORK(&backlight->work, ims_pcu_backlight_work);
snprintf(backlight->name, sizeof(backlight->name),
"pcu%d::kbd_backlight", pcu->device_no);
backlight->cdev.name = backlight->name;
backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
- backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+ backlight->cdev.brightness_set_blocking =
+ ims_pcu_backlight_set_brightness;
error = led_classdev_register(pcu->dev, &backlight->cdev);
if (error) {
@@ -1040,7 +1027,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
led_classdev_unregister(&backlight->cdev);
- cancel_work_sync(&backlight->work);
}
@@ -1064,7 +1050,7 @@ static ssize_t ims_pcu_attribute_show(struct device *dev,
container_of(dattr, struct ims_pcu_attribute, dattr);
char *field = (char *)pcu + attr->field_offset;
- return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field);
+ return sysfs_emit(buf, "%.*s\n", attr->field_length, field);
}
static ssize_t ims_pcu_attribute_store(struct device *dev,
@@ -1087,24 +1073,23 @@ static ssize_t ims_pcu_attribute_store(struct device *dev,
if (data_len > attr->field_length)
return -EINVAL;
- error = mutex_lock_interruptible(&pcu->cmd_mutex);
- if (error)
- return error;
-
- memset(field, 0, attr->field_length);
- memcpy(field, buf, data_len);
+ scoped_cond_guard(mutex_intr, return -EINTR, &pcu->cmd_mutex) {
+ memset(field, 0, attr->field_length);
+ memcpy(field, buf, data_len);
- error = ims_pcu_set_info(pcu);
+ error = ims_pcu_set_info(pcu);
- /*
- * Even if update failed, let's fetch the info again as we just
- * clobbered one of the fields.
- */
- ims_pcu_get_info(pcu);
+ /*
+ * Even if update failed, let's fetch the info again as we just
+ * clobbered one of the fields.
+ */
+ ims_pcu_get_info(pcu);
- mutex_unlock(&pcu->cmd_mutex);
+ if (error)
+ return error;
+ }
- return error < 0 ? error : count;
+ return count;
}
#define IMS_PCU_ATTR(_field, _mode) \
@@ -1167,7 +1152,6 @@ static ssize_t ims_pcu_update_firmware_store(struct device *dev,
{
struct usb_interface *intf = to_usb_interface(dev);
struct ims_pcu *pcu = usb_get_intfdata(intf);
- const struct firmware *fw = NULL;
int value;
int error;
@@ -1178,35 +1162,33 @@ static ssize_t ims_pcu_update_firmware_store(struct device *dev,
if (value != 1)
return -EINVAL;
- error = mutex_lock_interruptible(&pcu->cmd_mutex);
- if (error)
- return error;
-
+ const struct firmware *fw __free(firmware) = NULL;
error = request_ihex_firmware(&fw, IMS_PCU_FIRMWARE_NAME, pcu->dev);
if (error) {
dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n",
IMS_PCU_FIRMWARE_NAME, error);
- goto out;
+ return error;
}
- /*
- * If we are already in bootloader mode we can proceed with
- * flashing the firmware.
- *
- * If we are in application mode, then we need to switch into
- * bootloader mode, which will cause the device to disconnect
- * and reconnect as different device.
- */
- if (pcu->bootloader_mode)
- error = ims_pcu_handle_firmware_update(pcu, fw);
- else
- error = ims_pcu_switch_to_bootloader(pcu);
+ scoped_cond_guard(mutex_intr, return -EINTR, &pcu->cmd_mutex) {
+ /*
+ * If we are already in bootloader mode we can proceed with
+ * flashing the firmware.
+ *
+ * If we are in application mode, then we need to switch into
+ * bootloader mode, which will cause the device to disconnect
+ * and reconnect as different device.
+ */
+ if (pcu->bootloader_mode)
+ error = ims_pcu_handle_firmware_update(pcu, fw);
+ else
+ error = ims_pcu_switch_to_bootloader(pcu);
- release_firmware(fw);
+ if (error)
+ return error;
+ }
-out:
- mutex_unlock(&pcu->cmd_mutex);
- return error ?: count;
+ return count;
}
static DEVICE_ATTR(update_firmware, S_IWUSR,
@@ -1220,7 +1202,7 @@ ims_pcu_update_firmware_status_show(struct device *dev,
struct usb_interface *intf = to_usb_interface(dev);
struct ims_pcu *pcu = usb_get_intfdata(intf);
- return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status);
+ return sysfs_emit(buf, "%d\n", pcu->update_firmware_status);
}
static DEVICE_ATTR(update_firmware_status, S_IRUGO,
@@ -1242,7 +1224,7 @@ static struct attribute *ims_pcu_attrs[] = {
static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct usb_interface *intf = to_usb_interface(dev);
struct ims_pcu *pcu = usb_get_intfdata(intf);
umode_t mode = attr->mode;
@@ -1316,14 +1298,13 @@ static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev,
int error;
u8 data;
- mutex_lock(&pcu->cmd_mutex);
- error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
- mutex_unlock(&pcu->cmd_mutex);
-
- if (error)
- return error;
+ scoped_guard(mutex, &pcu->cmd_mutex) {
+ error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
+ if (error)
+ return error;
+ }
- return scnprintf(buf, PAGE_SIZE, "%x\n", data);
+ return sysfs_emit(buf, "%x\n", data);
}
static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
@@ -1339,11 +1320,13 @@ static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
if (error)
return error;
- mutex_lock(&pcu->cmd_mutex);
+ guard(mutex)(&pcu->cmd_mutex);
+
error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
- mutex_unlock(&pcu->cmd_mutex);
+ if (error)
+ return error;
- return error ?: count;
+ return count;
}
static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR,
@@ -1355,13 +1338,10 @@ static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev,
{
struct usb_interface *intf = to_usb_interface(dev);
struct ims_pcu *pcu = usb_get_intfdata(intf);
- int error;
- mutex_lock(&pcu->cmd_mutex);
- error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr);
- mutex_unlock(&pcu->cmd_mutex);
+ guard(mutex)(&pcu->cmd_mutex);
- return error;
+ return sysfs_emit(buf, "%x\n", pcu->ofn_reg_addr);
}
static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
@@ -1377,9 +1357,9 @@ static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
if (error)
return error;
- mutex_lock(&pcu->cmd_mutex);
+ guard(mutex)(&pcu->cmd_mutex);
+
pcu->ofn_reg_addr = value;
- mutex_unlock(&pcu->cmd_mutex);
return count;
}
@@ -1404,14 +1384,13 @@ static ssize_t ims_pcu_ofn_bit_show(struct device *dev,
int error;
u8 data;
- mutex_lock(&pcu->cmd_mutex);
- error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
- mutex_unlock(&pcu->cmd_mutex);
-
- if (error)
- return error;
+ scoped_guard(mutex, &pcu->cmd_mutex) {
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ if (error)
+ return error;
+ }
- return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
+ return sysfs_emit(buf, "%d\n", !!(data & (1 << attr->nr)));
}
static ssize_t ims_pcu_ofn_bit_store(struct device *dev,
@@ -1433,21 +1412,22 @@ static ssize_t ims_pcu_ofn_bit_store(struct device *dev,
if (value > 1)
return -EINVAL;
- mutex_lock(&pcu->cmd_mutex);
+ scoped_guard(mutex, &pcu->cmd_mutex) {
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ if (error)
+ return error;
- error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
- if (!error) {
if (value)
data |= 1U << attr->nr;
else
data &= ~(1U << attr->nr);
error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
+ if (error)
+ return error;
}
- mutex_unlock(&pcu->cmd_mutex);
-
- return error ?: count;
+ return count;
}
#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \
@@ -1480,9 +1460,27 @@ static struct attribute *ims_pcu_ofn_attrs[] = {
NULL
};
+static umode_t ims_pcu_ofn_is_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ umode_t mode = attr->mode;
+
+ /*
+ * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor.
+ */
+ if (pcu->bootloader_mode || pcu->device_id == IMS_PCU_PCU_B_DEVICE_ID)
+ mode = 0;
+
+ return mode;
+}
+
static const struct attribute_group ims_pcu_ofn_attr_group = {
- .name = "ofn",
- .attrs = ims_pcu_ofn_attrs,
+ .name = "ofn",
+ .is_visible = ims_pcu_ofn_is_attr_visible,
+ .attrs = ims_pcu_ofn_attrs,
};
static void ims_pcu_irq(struct urb *urb)
@@ -1904,16 +1902,6 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
/* Device appears to be operable, complete initialization */
pcu->device_no = atomic_inc_return(&device_no);
- /*
- * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
- */
- if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
- error = sysfs_create_group(&pcu->dev->kobj,
- &ims_pcu_ofn_attr_group);
- if (error)
- return error;
- }
-
error = ims_pcu_setup_backlight(pcu);
if (error)
return error;
@@ -1950,10 +1938,6 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
ims_pcu_destroy_gamepad(pcu);
ims_pcu_destroy_buttons(pcu);
ims_pcu_destroy_backlight(pcu);
-
- if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
- sysfs_remove_group(&pcu->dev->kobj,
- &ims_pcu_ofn_attr_group);
}
}
@@ -2007,7 +1991,7 @@ static int ims_pcu_probe(struct usb_interface *intf,
struct ims_pcu *pcu;
int error;
- pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL);
+ pcu = kzalloc(sizeof(*pcu), GFP_KERNEL);
if (!pcu)
return -ENOMEM;
@@ -2032,7 +2016,6 @@ static int ims_pcu_probe(struct usb_interface *intf,
}
usb_set_intfdata(pcu->ctrl_intf, pcu);
- usb_set_intfdata(pcu->data_intf, pcu);
error = ims_pcu_buffers_alloc(pcu);
if (error)
@@ -2046,20 +2029,14 @@ static int ims_pcu_probe(struct usb_interface *intf,
if (error)
goto err_stop_io;
- error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
- if (error)
- goto err_stop_io;
-
error = pcu->bootloader_mode ?
ims_pcu_init_bootloader_mode(pcu) :
ims_pcu_init_application_mode(pcu);
if (error)
- goto err_remove_sysfs;
+ goto err_stop_io;
return 0;
-err_remove_sysfs:
- sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
err_stop_io:
ims_pcu_stop_io(pcu);
err_free_buffers:
@@ -2085,8 +2062,6 @@ static void ims_pcu_disconnect(struct usb_interface *intf)
if (alt->desc.bInterfaceClass != USB_CLASS_COMM)
return;
- sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
-
ims_pcu_stop_io(pcu);
if (pcu->bootloader_mode)
@@ -2145,9 +2120,16 @@ static const struct usb_device_id ims_pcu_id_table[] = {
{ }
};
+static const struct attribute_group *ims_pcu_sysfs_groups[] = {
+ &ims_pcu_attr_group,
+ &ims_pcu_ofn_attr_group,
+ NULL
+};
+
static struct usb_driver ims_pcu_driver = {
.name = "ims_pcu",
.id_table = ims_pcu_id_table,
+ .dev_groups = ims_pcu_sysfs_groups,
.probe = ims_pcu_probe,
.disconnect = ims_pcu_disconnect,
#ifdef CONFIG_PM
diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c
new file mode 100644
index 000000000000..1851848e2cd3
--- /dev/null
+++ b/drivers/input/misc/iqs269a.c
@@ -0,0 +1,1976 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS269A Capacitive Touch Controller
+ *
+ * Copyright (C) 2020 Jeff LaBundy <jeff@labundy.com>
+ *
+ * This driver registers up to 3 input devices: one representing capacitive or
+ * inductive keys as well as Hall-effect switches, and one for each of the two
+ * axial sliders presented by the device.
+ */
+
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS269_VER_INFO 0x00
+#define IQS269_VER_INFO_PROD_NUM 0x4F
+#define IQS269_VER_INFO_FW_NUM_2 0x03
+#define IQS269_VER_INFO_FW_NUM_3 0x10
+
+#define IQS269_SYS_FLAGS 0x02
+#define IQS269_SYS_FLAGS_SHOW_RESET BIT(15)
+#define IQS269_SYS_FLAGS_PWR_MODE_MASK GENMASK(12, 11)
+#define IQS269_SYS_FLAGS_PWR_MODE_SHIFT 11
+#define IQS269_SYS_FLAGS_IN_ATI BIT(10)
+
+#define IQS269_CHx_COUNTS 0x08
+
+#define IQS269_SLIDER_X 0x30
+
+#define IQS269_CAL_DATA_A 0x35
+#define IQS269_CAL_DATA_A_HALL_BIN_L_MASK GENMASK(15, 12)
+#define IQS269_CAL_DATA_A_HALL_BIN_L_SHIFT 12
+#define IQS269_CAL_DATA_A_HALL_BIN_R_MASK GENMASK(11, 8)
+#define IQS269_CAL_DATA_A_HALL_BIN_R_SHIFT 8
+
+#define IQS269_SYS_SETTINGS 0x80
+#define IQS269_SYS_SETTINGS_CLK_DIV BIT(15)
+#define IQS269_SYS_SETTINGS_ULP_AUTO BIT(14)
+#define IQS269_SYS_SETTINGS_DIS_AUTO BIT(13)
+#define IQS269_SYS_SETTINGS_PWR_MODE_MASK GENMASK(12, 11)
+#define IQS269_SYS_SETTINGS_PWR_MODE_SHIFT 11
+#define IQS269_SYS_SETTINGS_PWR_MODE_MAX 3
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_MASK GENMASK(10, 8)
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT 8
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_MAX 7
+#define IQS269_SYS_SETTINGS_SLIDER_SWIPE BIT(7)
+#define IQS269_SYS_SETTINGS_RESEED_OFFSET BIT(6)
+#define IQS269_SYS_SETTINGS_EVENT_MODE BIT(5)
+#define IQS269_SYS_SETTINGS_EVENT_MODE_LP BIT(4)
+#define IQS269_SYS_SETTINGS_REDO_ATI BIT(2)
+#define IQS269_SYS_SETTINGS_ACK_RESET BIT(0)
+
+#define IQS269_FILT_STR_LP_LTA_MASK GENMASK(7, 6)
+#define IQS269_FILT_STR_LP_LTA_SHIFT 6
+#define IQS269_FILT_STR_LP_CNT_MASK GENMASK(5, 4)
+#define IQS269_FILT_STR_LP_CNT_SHIFT 4
+#define IQS269_FILT_STR_NP_LTA_MASK GENMASK(3, 2)
+#define IQS269_FILT_STR_NP_LTA_SHIFT 2
+#define IQS269_FILT_STR_NP_CNT_MASK GENMASK(1, 0)
+#define IQS269_FILT_STR_MAX 3
+
+#define IQS269_EVENT_MASK_SYS BIT(6)
+#define IQS269_EVENT_MASK_GESTURE BIT(3)
+#define IQS269_EVENT_MASK_DEEP BIT(2)
+#define IQS269_EVENT_MASK_TOUCH BIT(1)
+#define IQS269_EVENT_MASK_PROX BIT(0)
+
+#define IQS269_RATE_NP_MS_MAX 255
+#define IQS269_RATE_LP_MS_MAX 255
+#define IQS269_RATE_ULP_MS_MAX 4080
+#define IQS269_TIMEOUT_PWR_MS_MAX 130560
+#define IQS269_TIMEOUT_LTA_MS_MAX 130560
+
+#define IQS269_MISC_A_ATI_BAND_DISABLE BIT(15)
+#define IQS269_MISC_A_ATI_LP_ONLY BIT(14)
+#define IQS269_MISC_A_ATI_BAND_TIGHTEN BIT(13)
+#define IQS269_MISC_A_FILT_DISABLE BIT(12)
+#define IQS269_MISC_A_GPIO3_SELECT_MASK GENMASK(10, 8)
+#define IQS269_MISC_A_GPIO3_SELECT_SHIFT 8
+#define IQS269_MISC_A_DUAL_DIR BIT(6)
+#define IQS269_MISC_A_TX_FREQ_MASK GENMASK(5, 4)
+#define IQS269_MISC_A_TX_FREQ_SHIFT 4
+#define IQS269_MISC_A_TX_FREQ_MAX 3
+#define IQS269_MISC_A_GLOBAL_CAP_SIZE BIT(0)
+
+#define IQS269_MISC_B_RESEED_UI_SEL_MASK GENMASK(7, 6)
+#define IQS269_MISC_B_RESEED_UI_SEL_SHIFT 6
+#define IQS269_MISC_B_RESEED_UI_SEL_MAX 3
+#define IQS269_MISC_B_TRACKING_UI_ENABLE BIT(4)
+#define IQS269_MISC_B_FILT_STR_SLIDER GENMASK(1, 0)
+
+#define IQS269_TOUCH_HOLD_SLIDER_SEL 0x89
+#define IQS269_TOUCH_HOLD_DEFAULT 0x14
+#define IQS269_TOUCH_HOLD_MS_MIN 256
+#define IQS269_TOUCH_HOLD_MS_MAX 65280
+
+#define IQS269_TIMEOUT_TAP_MS_MAX 4080
+#define IQS269_TIMEOUT_SWIPE_MS_MAX 4080
+#define IQS269_THRESH_SWIPE_MAX 255
+
+#define IQS269_CHx_ENG_A_MEAS_CAP_SIZE BIT(15)
+#define IQS269_CHx_ENG_A_RX_GND_INACTIVE BIT(13)
+#define IQS269_CHx_ENG_A_LOCAL_CAP_SIZE BIT(12)
+#define IQS269_CHx_ENG_A_ATI_MODE_MASK GENMASK(9, 8)
+#define IQS269_CHx_ENG_A_ATI_MODE_SHIFT 8
+#define IQS269_CHx_ENG_A_ATI_MODE_MAX 3
+#define IQS269_CHx_ENG_A_INV_LOGIC BIT(7)
+#define IQS269_CHx_ENG_A_PROJ_BIAS_MASK GENMASK(6, 5)
+#define IQS269_CHx_ENG_A_PROJ_BIAS_SHIFT 5
+#define IQS269_CHx_ENG_A_PROJ_BIAS_MAX 3
+#define IQS269_CHx_ENG_A_SENSE_MODE_MASK GENMASK(3, 0)
+#define IQS269_CHx_ENG_A_SENSE_MODE_MAX 15
+
+#define IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE BIT(13)
+#define IQS269_CHx_ENG_B_SENSE_FREQ_MASK GENMASK(10, 9)
+#define IQS269_CHx_ENG_B_SENSE_FREQ_SHIFT 9
+#define IQS269_CHx_ENG_B_SENSE_FREQ_MAX 3
+#define IQS269_CHx_ENG_B_STATIC_ENABLE BIT(8)
+#define IQS269_CHx_ENG_B_ATI_BASE_MASK GENMASK(7, 6)
+#define IQS269_CHx_ENG_B_ATI_BASE_75 0x00
+#define IQS269_CHx_ENG_B_ATI_BASE_100 0x40
+#define IQS269_CHx_ENG_B_ATI_BASE_150 0x80
+#define IQS269_CHx_ENG_B_ATI_BASE_200 0xC0
+#define IQS269_CHx_ENG_B_ATI_TARGET_MASK GENMASK(5, 0)
+#define IQS269_CHx_ENG_B_ATI_TARGET_MAX 2016
+
+#define IQS269_CHx_WEIGHT_MAX 255
+#define IQS269_CHx_THRESH_MAX 255
+#define IQS269_CHx_HYST_DEEP_MASK GENMASK(7, 4)
+#define IQS269_CHx_HYST_DEEP_SHIFT 4
+#define IQS269_CHx_HYST_TOUCH_MASK GENMASK(3, 0)
+#define IQS269_CHx_HYST_MAX 15
+
+#define IQS269_CHx_HALL_INACTIVE 6
+#define IQS269_CHx_HALL_ACTIVE 7
+
+#define IQS269_HALL_PAD_R BIT(0)
+#define IQS269_HALL_PAD_L BIT(1)
+#define IQS269_HALL_PAD_INV BIT(6)
+
+#define IQS269_HALL_UI 0xF5
+#define IQS269_HALL_UI_ENABLE BIT(15)
+
+#define IQS269_MAX_REG 0xFF
+
+#define IQS269_OTP_OPTION_DEFAULT 0x00
+#define IQS269_OTP_OPTION_TWS 0xD0
+#define IQS269_OTP_OPTION_HOLD BIT(7)
+
+#define IQS269_NUM_CH 8
+#define IQS269_NUM_SL 2
+
+#define iqs269_irq_wait() usleep_range(200, 250)
+
+enum iqs269_local_cap_size {
+ IQS269_LOCAL_CAP_SIZE_0,
+ IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY,
+ IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5,
+};
+
+enum iqs269_st_offs {
+ IQS269_ST_OFFS_PROX,
+ IQS269_ST_OFFS_DIR,
+ IQS269_ST_OFFS_TOUCH,
+ IQS269_ST_OFFS_DEEP,
+};
+
+enum iqs269_th_offs {
+ IQS269_TH_OFFS_PROX,
+ IQS269_TH_OFFS_TOUCH,
+ IQS269_TH_OFFS_DEEP,
+};
+
+enum iqs269_event_id {
+ IQS269_EVENT_PROX_DN,
+ IQS269_EVENT_PROX_UP,
+ IQS269_EVENT_TOUCH_DN,
+ IQS269_EVENT_TOUCH_UP,
+ IQS269_EVENT_DEEP_DN,
+ IQS269_EVENT_DEEP_UP,
+};
+
+enum iqs269_slider_id {
+ IQS269_SLIDER_NONE,
+ IQS269_SLIDER_KEY,
+ IQS269_SLIDER_RAW,
+};
+
+enum iqs269_gesture_id {
+ IQS269_GESTURE_TAP,
+ IQS269_GESTURE_HOLD,
+ IQS269_GESTURE_FLICK_POS,
+ IQS269_GESTURE_FLICK_NEG,
+ IQS269_NUM_GESTURES,
+};
+
+struct iqs269_switch_desc {
+ unsigned int code;
+ bool enabled;
+};
+
+struct iqs269_event_desc {
+ const char *name;
+ enum iqs269_st_offs st_offs;
+ enum iqs269_th_offs th_offs;
+ bool dir_up;
+ u8 mask;
+};
+
+static const struct iqs269_event_desc iqs269_events[] = {
+ [IQS269_EVENT_PROX_DN] = {
+ .name = "event-prox",
+ .st_offs = IQS269_ST_OFFS_PROX,
+ .th_offs = IQS269_TH_OFFS_PROX,
+ .mask = IQS269_EVENT_MASK_PROX,
+ },
+ [IQS269_EVENT_PROX_UP] = {
+ .name = "event-prox-alt",
+ .st_offs = IQS269_ST_OFFS_PROX,
+ .th_offs = IQS269_TH_OFFS_PROX,
+ .dir_up = true,
+ .mask = IQS269_EVENT_MASK_PROX,
+ },
+ [IQS269_EVENT_TOUCH_DN] = {
+ .name = "event-touch",
+ .st_offs = IQS269_ST_OFFS_TOUCH,
+ .th_offs = IQS269_TH_OFFS_TOUCH,
+ .mask = IQS269_EVENT_MASK_TOUCH,
+ },
+ [IQS269_EVENT_TOUCH_UP] = {
+ .name = "event-touch-alt",
+ .st_offs = IQS269_ST_OFFS_TOUCH,
+ .th_offs = IQS269_TH_OFFS_TOUCH,
+ .dir_up = true,
+ .mask = IQS269_EVENT_MASK_TOUCH,
+ },
+ [IQS269_EVENT_DEEP_DN] = {
+ .name = "event-deep",
+ .st_offs = IQS269_ST_OFFS_DEEP,
+ .th_offs = IQS269_TH_OFFS_DEEP,
+ .mask = IQS269_EVENT_MASK_DEEP,
+ },
+ [IQS269_EVENT_DEEP_UP] = {
+ .name = "event-deep-alt",
+ .st_offs = IQS269_ST_OFFS_DEEP,
+ .th_offs = IQS269_TH_OFFS_DEEP,
+ .dir_up = true,
+ .mask = IQS269_EVENT_MASK_DEEP,
+ },
+};
+
+struct iqs269_ver_info {
+ u8 prod_num;
+ u8 sw_num;
+ u8 hw_num;
+ u8 fw_num;
+} __packed;
+
+struct iqs269_ch_reg {
+ u8 rx_enable;
+ u8 tx_enable;
+ __be16 engine_a;
+ __be16 engine_b;
+ __be16 ati_comp;
+ u8 thresh[3];
+ u8 hyst;
+ u8 assoc_select;
+ u8 assoc_weight;
+} __packed;
+
+struct iqs269_sys_reg {
+ __be16 general;
+ u8 active;
+ u8 filter;
+ u8 reseed;
+ u8 event_mask;
+ u8 rate_np;
+ u8 rate_lp;
+ u8 rate_ulp;
+ u8 timeout_pwr;
+ u8 timeout_rdy;
+ u8 timeout_lta;
+ __be16 misc_a;
+ __be16 misc_b;
+ u8 blocking;
+ u8 padding;
+ u8 slider_select[IQS269_NUM_SL];
+ u8 timeout_tap;
+ u8 timeout_swipe;
+ u8 thresh_swipe;
+ u8 redo_ati;
+ struct iqs269_ch_reg ch_reg[IQS269_NUM_CH];
+} __packed;
+
+struct iqs269_flags {
+ __be16 system;
+ u8 gesture;
+ u8 padding;
+ u8 states[4];
+} __packed;
+
+struct iqs269_private {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct mutex lock;
+ struct iqs269_switch_desc switches[ARRAY_SIZE(iqs269_events)];
+ struct iqs269_ver_info ver_info;
+ struct iqs269_sys_reg sys_reg;
+ struct completion ati_done;
+ struct input_dev *keypad;
+ struct input_dev *slider[IQS269_NUM_SL];
+ unsigned int keycode[ARRAY_SIZE(iqs269_events) * IQS269_NUM_CH];
+ unsigned int sl_code[IQS269_NUM_SL][IQS269_NUM_GESTURES];
+ unsigned int otp_option;
+ unsigned int ch_num;
+ bool hall_enable;
+ bool ati_current;
+};
+
+static enum iqs269_slider_id iqs269_slider_type(struct iqs269_private *iqs269,
+ int slider_num)
+{
+ int i;
+
+ /*
+ * Slider 1 is unavailable if the touch-and-hold option is enabled via
+ * OTP. In that case, the channel selection register is repurposed for
+ * the touch-and-hold timer ceiling.
+ */
+ if (slider_num && (iqs269->otp_option & IQS269_OTP_OPTION_HOLD))
+ return IQS269_SLIDER_NONE;
+
+ if (!iqs269->sys_reg.slider_select[slider_num])
+ return IQS269_SLIDER_NONE;
+
+ for (i = 0; i < IQS269_NUM_GESTURES; i++)
+ if (iqs269->sl_code[slider_num][i] != KEY_RESERVED)
+ return IQS269_SLIDER_KEY;
+
+ return IQS269_SLIDER_RAW;
+}
+
+static int iqs269_ati_mode_set(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int mode)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_a;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ if (mode > IQS269_CHx_ENG_A_ATI_MODE_MAX)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_a = be16_to_cpu(ch_reg[ch_num].engine_a);
+
+ engine_a &= ~IQS269_CHx_ENG_A_ATI_MODE_MASK;
+ engine_a |= (mode << IQS269_CHx_ENG_A_ATI_MODE_SHIFT);
+
+ ch_reg[ch_num].engine_a = cpu_to_be16(engine_a);
+ iqs269->ati_current = false;
+
+ return 0;
+}
+
+static int iqs269_ati_mode_get(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int *mode)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_a;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_a = be16_to_cpu(ch_reg[ch_num].engine_a);
+
+ engine_a &= IQS269_CHx_ENG_A_ATI_MODE_MASK;
+ *mode = (engine_a >> IQS269_CHx_ENG_A_ATI_MODE_SHIFT);
+
+ return 0;
+}
+
+static int iqs269_ati_base_set(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int base)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_b;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ switch (base) {
+ case 75:
+ base = IQS269_CHx_ENG_B_ATI_BASE_75;
+ break;
+
+ case 100:
+ base = IQS269_CHx_ENG_B_ATI_BASE_100;
+ break;
+
+ case 150:
+ base = IQS269_CHx_ENG_B_ATI_BASE_150;
+ break;
+
+ case 200:
+ base = IQS269_CHx_ENG_B_ATI_BASE_200;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_b = be16_to_cpu(ch_reg[ch_num].engine_b);
+
+ engine_b &= ~IQS269_CHx_ENG_B_ATI_BASE_MASK;
+ engine_b |= base;
+
+ ch_reg[ch_num].engine_b = cpu_to_be16(engine_b);
+ iqs269->ati_current = false;
+
+ return 0;
+}
+
+static int iqs269_ati_base_get(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int *base)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_b;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_b = be16_to_cpu(ch_reg[ch_num].engine_b);
+
+ switch (engine_b & IQS269_CHx_ENG_B_ATI_BASE_MASK) {
+ case IQS269_CHx_ENG_B_ATI_BASE_75:
+ *base = 75;
+ return 0;
+
+ case IQS269_CHx_ENG_B_ATI_BASE_100:
+ *base = 100;
+ return 0;
+
+ case IQS269_CHx_ENG_B_ATI_BASE_150:
+ *base = 150;
+ return 0;
+
+ case IQS269_CHx_ENG_B_ATI_BASE_200:
+ *base = 200;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iqs269_ati_target_set(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int target)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_b;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ if (target > IQS269_CHx_ENG_B_ATI_TARGET_MAX)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_b = be16_to_cpu(ch_reg[ch_num].engine_b);
+
+ engine_b &= ~IQS269_CHx_ENG_B_ATI_TARGET_MASK;
+ engine_b |= target / 32;
+
+ ch_reg[ch_num].engine_b = cpu_to_be16(engine_b);
+ iqs269->ati_current = false;
+
+ return 0;
+}
+
+static int iqs269_ati_target_get(struct iqs269_private *iqs269,
+ unsigned int ch_num, unsigned int *target)
+{
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ u16 engine_b;
+
+ if (ch_num >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ engine_b = be16_to_cpu(ch_reg[ch_num].engine_b);
+ *target = (engine_b & IQS269_CHx_ENG_B_ATI_TARGET_MASK) * 32;
+
+ return 0;
+}
+
+static int iqs269_parse_mask(const struct fwnode_handle *fwnode,
+ const char *propname, u8 *mask)
+{
+ unsigned int val[IQS269_NUM_CH];
+ int count, error, i;
+
+ count = fwnode_property_count_u32(fwnode, propname);
+ if (count < 0)
+ return 0;
+
+ if (count > IQS269_NUM_CH)
+ return -EINVAL;
+
+ error = fwnode_property_read_u32_array(fwnode, propname, val, count);
+ if (error)
+ return error;
+
+ *mask = 0;
+
+ for (i = 0; i < count; i++) {
+ if (val[i] >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ *mask |= BIT(val[i]);
+ }
+
+ return 0;
+}
+
+static int iqs269_parse_chan(struct iqs269_private *iqs269,
+ const struct fwnode_handle *ch_node)
+{
+ struct i2c_client *client = iqs269->client;
+ struct iqs269_ch_reg *ch_reg;
+ u16 engine_a, engine_b;
+ unsigned int reg, val;
+ int error, i;
+
+ error = fwnode_property_read_u32(ch_node, "reg", &reg);
+ if (error) {
+ dev_err(&client->dev, "Failed to read channel number: %d\n",
+ error);
+ return error;
+ } else if (reg >= IQS269_NUM_CH) {
+ dev_err(&client->dev, "Invalid channel number: %u\n", reg);
+ return -EINVAL;
+ }
+
+ iqs269->sys_reg.active |= BIT(reg);
+ if (!fwnode_property_present(ch_node, "azoteq,reseed-disable"))
+ iqs269->sys_reg.reseed |= BIT(reg);
+
+ if (fwnode_property_present(ch_node, "azoteq,blocking-enable"))
+ iqs269->sys_reg.blocking |= BIT(reg);
+
+ if (fwnode_property_present(ch_node, "azoteq,slider0-select"))
+ iqs269->sys_reg.slider_select[0] |= BIT(reg);
+
+ if (fwnode_property_present(ch_node, "azoteq,slider1-select") &&
+ !(iqs269->otp_option & IQS269_OTP_OPTION_HOLD))
+ iqs269->sys_reg.slider_select[1] |= BIT(reg);
+
+ ch_reg = &iqs269->sys_reg.ch_reg[reg];
+
+ error = iqs269_parse_mask(ch_node, "azoteq,rx-enable",
+ &ch_reg->rx_enable);
+ if (error) {
+ dev_err(&client->dev, "Invalid channel %u RX enable mask: %d\n",
+ reg, error);
+ return error;
+ }
+
+ error = iqs269_parse_mask(ch_node, "azoteq,tx-enable",
+ &ch_reg->tx_enable);
+ if (error) {
+ dev_err(&client->dev, "Invalid channel %u TX enable mask: %d\n",
+ reg, error);
+ return error;
+ }
+
+ engine_a = be16_to_cpu(ch_reg->engine_a);
+ engine_b = be16_to_cpu(ch_reg->engine_b);
+
+ engine_a |= IQS269_CHx_ENG_A_MEAS_CAP_SIZE;
+ if (fwnode_property_present(ch_node, "azoteq,meas-cap-decrease"))
+ engine_a &= ~IQS269_CHx_ENG_A_MEAS_CAP_SIZE;
+
+ engine_a |= IQS269_CHx_ENG_A_RX_GND_INACTIVE;
+ if (fwnode_property_present(ch_node, "azoteq,rx-float-inactive"))
+ engine_a &= ~IQS269_CHx_ENG_A_RX_GND_INACTIVE;
+
+ engine_a &= ~IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
+ engine_b &= ~IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
+ if (!fwnode_property_read_u32(ch_node, "azoteq,local-cap-size", &val)) {
+ switch (val) {
+ case IQS269_LOCAL_CAP_SIZE_0:
+ break;
+
+ case IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5:
+ engine_a |= IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
+ fallthrough;
+
+ case IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY:
+ engine_b |= IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
+ break;
+
+ default:
+ dev_err(&client->dev,
+ "Invalid channel %u local cap. size: %u\n", reg,
+ val);
+ return -EINVAL;
+ }
+ }
+
+ engine_a &= ~IQS269_CHx_ENG_A_INV_LOGIC;
+ if (fwnode_property_present(ch_node, "azoteq,invert-enable"))
+ engine_a |= IQS269_CHx_ENG_A_INV_LOGIC;
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,proj-bias", &val)) {
+ if (val > IQS269_CHx_ENG_A_PROJ_BIAS_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u bias current: %u\n", reg,
+ val);
+ return -EINVAL;
+ }
+
+ engine_a &= ~IQS269_CHx_ENG_A_PROJ_BIAS_MASK;
+ engine_a |= (val << IQS269_CHx_ENG_A_PROJ_BIAS_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,sense-mode", &val)) {
+ if (val > IQS269_CHx_ENG_A_SENSE_MODE_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u sensing mode: %u\n", reg,
+ val);
+ return -EINVAL;
+ }
+
+ engine_a &= ~IQS269_CHx_ENG_A_SENSE_MODE_MASK;
+ engine_a |= val;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,sense-freq", &val)) {
+ if (val > IQS269_CHx_ENG_B_SENSE_FREQ_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u sensing frequency: %u\n",
+ reg, val);
+ return -EINVAL;
+ }
+
+ engine_b &= ~IQS269_CHx_ENG_B_SENSE_FREQ_MASK;
+ engine_b |= (val << IQS269_CHx_ENG_B_SENSE_FREQ_SHIFT);
+ }
+
+ engine_b &= ~IQS269_CHx_ENG_B_STATIC_ENABLE;
+ if (fwnode_property_present(ch_node, "azoteq,static-enable"))
+ engine_b |= IQS269_CHx_ENG_B_STATIC_ENABLE;
+
+ ch_reg->engine_a = cpu_to_be16(engine_a);
+ ch_reg->engine_b = cpu_to_be16(engine_b);
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,ati-mode", &val)) {
+ error = iqs269_ati_mode_set(iqs269, reg, val);
+ if (error) {
+ dev_err(&client->dev,
+ "Invalid channel %u ATI mode: %u\n", reg, val);
+ return error;
+ }
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,ati-base", &val)) {
+ error = iqs269_ati_base_set(iqs269, reg, val);
+ if (error) {
+ dev_err(&client->dev,
+ "Invalid channel %u ATI base: %u\n", reg, val);
+ return error;
+ }
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,ati-target", &val)) {
+ error = iqs269_ati_target_set(iqs269, reg, val);
+ if (error) {
+ dev_err(&client->dev,
+ "Invalid channel %u ATI target: %u\n", reg,
+ val);
+ return error;
+ }
+ }
+
+ error = iqs269_parse_mask(ch_node, "azoteq,assoc-select",
+ &ch_reg->assoc_select);
+ if (error) {
+ dev_err(&client->dev, "Invalid channel %u association: %d\n",
+ reg, error);
+ return error;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,assoc-weight", &val)) {
+ if (val > IQS269_CHx_WEIGHT_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u associated weight: %u\n",
+ reg, val);
+ return -EINVAL;
+ }
+
+ ch_reg->assoc_weight = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+ struct fwnode_handle *ev_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(ch_node,
+ iqs269_events[i].name);
+ if (!ev_node)
+ continue;
+
+ if (!fwnode_property_read_u32(ev_node, "azoteq,thresh", &val)) {
+ if (val > IQS269_CHx_THRESH_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u threshold: %u\n",
+ reg, val);
+ return -EINVAL;
+ }
+
+ ch_reg->thresh[iqs269_events[i].th_offs] = val;
+ }
+
+ if (!fwnode_property_read_u32(ev_node, "azoteq,hyst", &val)) {
+ u8 *hyst = &ch_reg->hyst;
+
+ if (val > IQS269_CHx_HYST_MAX) {
+ dev_err(&client->dev,
+ "Invalid channel %u hysteresis: %u\n",
+ reg, val);
+ return -EINVAL;
+ }
+
+ if (i == IQS269_EVENT_DEEP_DN ||
+ i == IQS269_EVENT_DEEP_UP) {
+ *hyst &= ~IQS269_CHx_HYST_DEEP_MASK;
+ *hyst |= (val << IQS269_CHx_HYST_DEEP_SHIFT);
+ } else if (i == IQS269_EVENT_TOUCH_DN ||
+ i == IQS269_EVENT_TOUCH_UP) {
+ *hyst &= ~IQS269_CHx_HYST_TOUCH_MASK;
+ *hyst |= val;
+ }
+ }
+
+ error = fwnode_property_read_u32(ev_node, "linux,code", &val);
+ if (error == -EINVAL) {
+ continue;
+ } else if (error) {
+ dev_err(&client->dev,
+ "Failed to read channel %u code: %d\n", reg,
+ error);
+ return error;
+ }
+
+ switch (reg) {
+ case IQS269_CHx_HALL_ACTIVE:
+ if (iqs269->hall_enable) {
+ iqs269->switches[i].code = val;
+ iqs269->switches[i].enabled = true;
+ }
+ fallthrough;
+
+ case IQS269_CHx_HALL_INACTIVE:
+ if (iqs269->hall_enable)
+ break;
+ fallthrough;
+
+ default:
+ iqs269->keycode[i * IQS269_NUM_CH + reg] = val;
+ }
+
+ iqs269->sys_reg.event_mask &= ~iqs269_events[i].mask;
+ }
+
+ return 0;
+}
+
+static int iqs269_parse_prop(struct iqs269_private *iqs269)
+{
+ struct iqs269_sys_reg *sys_reg = &iqs269->sys_reg;
+ struct i2c_client *client = iqs269->client;
+ u16 general, misc_a, misc_b;
+ unsigned int val;
+ int error;
+
+ iqs269->hall_enable = device_property_present(&client->dev,
+ "azoteq,hall-enable");
+
+ error = regmap_raw_read(iqs269->regmap, IQS269_SYS_SETTINGS, sys_reg,
+ sizeof(*sys_reg));
+ if (error)
+ return error;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,filt-str-lp-lta",
+ &val)) {
+ if (val > IQS269_FILT_STR_MAX) {
+ dev_err(&client->dev, "Invalid filter strength: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->filter &= ~IQS269_FILT_STR_LP_LTA_MASK;
+ sys_reg->filter |= (val << IQS269_FILT_STR_LP_LTA_SHIFT);
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,filt-str-lp-cnt",
+ &val)) {
+ if (val > IQS269_FILT_STR_MAX) {
+ dev_err(&client->dev, "Invalid filter strength: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->filter &= ~IQS269_FILT_STR_LP_CNT_MASK;
+ sys_reg->filter |= (val << IQS269_FILT_STR_LP_CNT_SHIFT);
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,filt-str-np-lta",
+ &val)) {
+ if (val > IQS269_FILT_STR_MAX) {
+ dev_err(&client->dev, "Invalid filter strength: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->filter &= ~IQS269_FILT_STR_NP_LTA_MASK;
+ sys_reg->filter |= (val << IQS269_FILT_STR_NP_LTA_SHIFT);
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,filt-str-np-cnt",
+ &val)) {
+ if (val > IQS269_FILT_STR_MAX) {
+ dev_err(&client->dev, "Invalid filter strength: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->filter &= ~IQS269_FILT_STR_NP_CNT_MASK;
+ sys_reg->filter |= val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-np-ms",
+ &val)) {
+ if (val > IQS269_RATE_NP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_np = val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-lp-ms",
+ &val)) {
+ if (val > IQS269_RATE_LP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_lp = val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-ulp-ms",
+ &val)) {
+ if (val > IQS269_RATE_ULP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_ulp = val / 16;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,timeout-pwr-ms",
+ &val)) {
+ if (val > IQS269_TIMEOUT_PWR_MS_MAX) {
+ dev_err(&client->dev, "Invalid timeout: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_pwr = val / 512;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,timeout-lta-ms",
+ &val)) {
+ if (val > IQS269_TIMEOUT_LTA_MS_MAX) {
+ dev_err(&client->dev, "Invalid timeout: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_lta = val / 512;
+ }
+
+ misc_a = be16_to_cpu(sys_reg->misc_a);
+ misc_b = be16_to_cpu(sys_reg->misc_b);
+
+ misc_a &= ~IQS269_MISC_A_ATI_BAND_DISABLE;
+ if (device_property_present(&client->dev, "azoteq,ati-band-disable"))
+ misc_a |= IQS269_MISC_A_ATI_BAND_DISABLE;
+
+ misc_a &= ~IQS269_MISC_A_ATI_LP_ONLY;
+ if (device_property_present(&client->dev, "azoteq,ati-lp-only"))
+ misc_a |= IQS269_MISC_A_ATI_LP_ONLY;
+
+ misc_a &= ~IQS269_MISC_A_ATI_BAND_TIGHTEN;
+ if (device_property_present(&client->dev, "azoteq,ati-band-tighten"))
+ misc_a |= IQS269_MISC_A_ATI_BAND_TIGHTEN;
+
+ misc_a &= ~IQS269_MISC_A_FILT_DISABLE;
+ if (device_property_present(&client->dev, "azoteq,filt-disable"))
+ misc_a |= IQS269_MISC_A_FILT_DISABLE;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,gpio3-select",
+ &val)) {
+ if (val >= IQS269_NUM_CH) {
+ dev_err(&client->dev, "Invalid GPIO3 selection: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ misc_a &= ~IQS269_MISC_A_GPIO3_SELECT_MASK;
+ misc_a |= (val << IQS269_MISC_A_GPIO3_SELECT_SHIFT);
+ }
+
+ misc_a &= ~IQS269_MISC_A_DUAL_DIR;
+ if (device_property_present(&client->dev, "azoteq,dual-direction"))
+ misc_a |= IQS269_MISC_A_DUAL_DIR;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,tx-freq", &val)) {
+ if (val > IQS269_MISC_A_TX_FREQ_MAX) {
+ dev_err(&client->dev,
+ "Invalid excitation frequency: %u\n", val);
+ return -EINVAL;
+ }
+
+ misc_a &= ~IQS269_MISC_A_TX_FREQ_MASK;
+ misc_a |= (val << IQS269_MISC_A_TX_FREQ_SHIFT);
+ }
+
+ misc_a &= ~IQS269_MISC_A_GLOBAL_CAP_SIZE;
+ if (device_property_present(&client->dev, "azoteq,global-cap-increase"))
+ misc_a |= IQS269_MISC_A_GLOBAL_CAP_SIZE;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,reseed-select",
+ &val)) {
+ if (val > IQS269_MISC_B_RESEED_UI_SEL_MAX) {
+ dev_err(&client->dev, "Invalid reseed selection: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ misc_b &= ~IQS269_MISC_B_RESEED_UI_SEL_MASK;
+ misc_b |= (val << IQS269_MISC_B_RESEED_UI_SEL_SHIFT);
+ }
+
+ misc_b &= ~IQS269_MISC_B_TRACKING_UI_ENABLE;
+ if (device_property_present(&client->dev, "azoteq,tracking-enable"))
+ misc_b |= IQS269_MISC_B_TRACKING_UI_ENABLE;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,filt-str-slider",
+ &val)) {
+ if (val > IQS269_FILT_STR_MAX) {
+ dev_err(&client->dev, "Invalid filter strength: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ misc_b &= ~IQS269_MISC_B_FILT_STR_SLIDER;
+ misc_b |= val;
+ }
+
+ sys_reg->misc_a = cpu_to_be16(misc_a);
+ sys_reg->misc_b = cpu_to_be16(misc_b);
+
+ sys_reg->active = 0;
+ sys_reg->reseed = 0;
+
+ sys_reg->blocking = 0;
+
+ sys_reg->slider_select[0] = 0;
+
+ /*
+ * If configured via OTP to do so, the device asserts a pulse on the
+ * GPIO4 pin for approximately 60 ms once a selected channel is held
+ * in a state of touch for a configurable length of time.
+ *
+ * In that case, the register used for slider 1 channel selection is
+ * repurposed for the touch-and-hold timer ceiling.
+ */
+ if (iqs269->otp_option & IQS269_OTP_OPTION_HOLD) {
+ if (!device_property_read_u32(&client->dev,
+ "azoteq,touch-hold-ms", &val)) {
+ if (val < IQS269_TOUCH_HOLD_MS_MIN ||
+ val > IQS269_TOUCH_HOLD_MS_MAX) {
+ dev_err(&client->dev,
+ "Invalid touch-and-hold ceiling: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->slider_select[1] = val / 256;
+ } else if (iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) {
+ /*
+ * The default touch-and-hold timer ceiling initially
+ * read from early revisions of silicon is invalid if
+ * the device experienced a soft reset between power-
+ * on and the read operation.
+ *
+ * To protect against this case, explicitly cache the
+ * default value so that it is restored each time the
+ * device is re-initialized.
+ */
+ sys_reg->slider_select[1] = IQS269_TOUCH_HOLD_DEFAULT;
+ }
+ } else {
+ sys_reg->slider_select[1] = 0;
+ }
+
+ sys_reg->event_mask = ~((u8)IQS269_EVENT_MASK_SYS);
+
+ device_for_each_child_node_scoped(&client->dev, ch_node) {
+ error = iqs269_parse_chan(iqs269, ch_node);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Volunteer all active channels to participate in ATI when REDO-ATI is
+ * manually triggered.
+ */
+ sys_reg->redo_ati = sys_reg->active;
+
+ general = be16_to_cpu(sys_reg->general);
+
+ if (device_property_present(&client->dev, "azoteq,clk-div"))
+ general |= IQS269_SYS_SETTINGS_CLK_DIV;
+
+ /*
+ * Configure the device to automatically switch between normal and low-
+ * power modes as a function of sensing activity. Ultra-low-power mode,
+ * if enabled, is reserved for suspend.
+ */
+ general &= ~IQS269_SYS_SETTINGS_ULP_AUTO;
+ general &= ~IQS269_SYS_SETTINGS_DIS_AUTO;
+ general &= ~IQS269_SYS_SETTINGS_PWR_MODE_MASK;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,suspend-mode",
+ &val)) {
+ if (val > IQS269_SYS_SETTINGS_PWR_MODE_MAX) {
+ dev_err(&client->dev, "Invalid suspend mode: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ general |= (val << IQS269_SYS_SETTINGS_PWR_MODE_SHIFT);
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,ulp-update",
+ &val)) {
+ if (val > IQS269_SYS_SETTINGS_ULP_UPDATE_MAX) {
+ dev_err(&client->dev, "Invalid update rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ general &= ~IQS269_SYS_SETTINGS_ULP_UPDATE_MASK;
+ general |= (val << IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT);
+ }
+
+ if (device_property_present(&client->dev, "linux,keycodes")) {
+ int scale = 1;
+ int count = device_property_count_u32(&client->dev,
+ "linux,keycodes");
+ if (count > IQS269_NUM_GESTURES * IQS269_NUM_SL) {
+ dev_err(&client->dev, "Too many keycodes present\n");
+ return -EINVAL;
+ } else if (count < 0) {
+ dev_err(&client->dev, "Failed to count keycodes: %d\n",
+ count);
+ return count;
+ }
+
+ error = device_property_read_u32_array(&client->dev,
+ "linux,keycodes",
+ *iqs269->sl_code, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read keycodes: %d\n",
+ error);
+ return error;
+ }
+
+ if (device_property_present(&client->dev,
+ "azoteq,gesture-swipe"))
+ general |= IQS269_SYS_SETTINGS_SLIDER_SWIPE;
+
+ /*
+ * Early revisions of silicon use a more granular step size for
+ * tap and swipe gesture timeouts; scale them appropriately.
+ */
+ if (iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3)
+ scale = 4;
+
+ if (!device_property_read_u32(&client->dev,
+ "azoteq,timeout-tap-ms", &val)) {
+ if (val > IQS269_TIMEOUT_TAP_MS_MAX / scale) {
+ dev_err(&client->dev, "Invalid timeout: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_tap = val / (16 / scale);
+ }
+
+ if (!device_property_read_u32(&client->dev,
+ "azoteq,timeout-swipe-ms",
+ &val)) {
+ if (val > IQS269_TIMEOUT_SWIPE_MS_MAX / scale) {
+ dev_err(&client->dev, "Invalid timeout: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_swipe = val / (16 / scale);
+ }
+
+ if (!device_property_read_u32(&client->dev,
+ "azoteq,thresh-swipe", &val)) {
+ if (val > IQS269_THRESH_SWIPE_MAX) {
+ dev_err(&client->dev, "Invalid threshold: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->thresh_swipe = val;
+ }
+
+ sys_reg->event_mask &= ~IQS269_EVENT_MASK_GESTURE;
+ }
+
+ general &= ~IQS269_SYS_SETTINGS_RESEED_OFFSET;
+ if (device_property_present(&client->dev, "azoteq,reseed-offset"))
+ general |= IQS269_SYS_SETTINGS_RESEED_OFFSET;
+
+ general |= IQS269_SYS_SETTINGS_EVENT_MODE;
+
+ /*
+ * As per the datasheet, enable streaming during normal-power mode if
+ * raw coordinates will be read from either slider. In that case, the
+ * device returns to event mode during low-power mode.
+ */
+ if (iqs269_slider_type(iqs269, 0) == IQS269_SLIDER_RAW ||
+ iqs269_slider_type(iqs269, 1) == IQS269_SLIDER_RAW)
+ general |= IQS269_SYS_SETTINGS_EVENT_MODE_LP;
+
+ general |= IQS269_SYS_SETTINGS_REDO_ATI;
+ general |= IQS269_SYS_SETTINGS_ACK_RESET;
+
+ sys_reg->general = cpu_to_be16(general);
+
+ return 0;
+}
+
+static const struct reg_sequence iqs269_tws_init[] = {
+ { IQS269_TOUCH_HOLD_SLIDER_SEL, IQS269_TOUCH_HOLD_DEFAULT },
+ { 0xF0, 0x580F },
+ { 0xF0, 0x59EF },
+};
+
+static int iqs269_dev_init(struct iqs269_private *iqs269)
+{
+ int error;
+
+ guard(mutex)(&iqs269->lock);
+
+ /*
+ * Early revisions of silicon require the following workaround in order
+ * to restore any OTP-enabled functionality after a soft reset.
+ */
+ if (iqs269->otp_option == IQS269_OTP_OPTION_TWS &&
+ iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) {
+ error = regmap_multi_reg_write(iqs269->regmap, iqs269_tws_init,
+ ARRAY_SIZE(iqs269_tws_init));
+ if (error)
+ return error;
+ }
+
+ error = regmap_update_bits(iqs269->regmap, IQS269_HALL_UI,
+ IQS269_HALL_UI_ENABLE,
+ iqs269->hall_enable ? ~0 : 0);
+ if (error)
+ return error;
+
+ error = regmap_raw_write(iqs269->regmap, IQS269_SYS_SETTINGS,
+ &iqs269->sys_reg, sizeof(iqs269->sys_reg));
+ if (error)
+ return error;
+
+ /*
+ * The following delay gives the device time to deassert its RDY output
+ * so as to prevent an interrupt from being serviced prematurely.
+ */
+ usleep_range(2000, 2100);
+
+ iqs269->ati_current = true;
+
+ return 0;
+}
+
+static int iqs269_input_init(struct iqs269_private *iqs269)
+{
+ struct i2c_client *client = iqs269->client;
+ unsigned int sw_code, keycode;
+ int error, i, j;
+
+ iqs269->keypad = devm_input_allocate_device(&client->dev);
+ if (!iqs269->keypad)
+ return -ENOMEM;
+
+ iqs269->keypad->keycodemax = ARRAY_SIZE(iqs269->keycode);
+ iqs269->keypad->keycode = iqs269->keycode;
+ iqs269->keypad->keycodesize = sizeof(*iqs269->keycode);
+
+ iqs269->keypad->name = "iqs269a_keypad";
+ iqs269->keypad->id.bustype = BUS_I2C;
+
+ for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+ sw_code = iqs269->switches[i].code;
+
+ for (j = 0; j < IQS269_NUM_CH; j++) {
+ keycode = iqs269->keycode[i * IQS269_NUM_CH + j];
+
+ /*
+ * Hall-effect sensing repurposes a pair of dedicated
+ * channels, only one of which reports events.
+ */
+ switch (j) {
+ case IQS269_CHx_HALL_ACTIVE:
+ if (iqs269->hall_enable &&
+ iqs269->switches[i].enabled)
+ input_set_capability(iqs269->keypad,
+ EV_SW, sw_code);
+ fallthrough;
+
+ case IQS269_CHx_HALL_INACTIVE:
+ if (iqs269->hall_enable)
+ continue;
+ fallthrough;
+
+ default:
+ if (keycode != KEY_RESERVED)
+ input_set_capability(iqs269->keypad,
+ EV_KEY, keycode);
+ }
+ }
+ }
+
+ for (i = 0; i < IQS269_NUM_SL; i++) {
+ if (iqs269_slider_type(iqs269, i) == IQS269_SLIDER_NONE)
+ continue;
+
+ iqs269->slider[i] = devm_input_allocate_device(&client->dev);
+ if (!iqs269->slider[i])
+ return -ENOMEM;
+
+ iqs269->slider[i]->keycodemax = ARRAY_SIZE(iqs269->sl_code[i]);
+ iqs269->slider[i]->keycode = iqs269->sl_code[i];
+ iqs269->slider[i]->keycodesize = sizeof(**iqs269->sl_code);
+
+ iqs269->slider[i]->name = i ? "iqs269a_slider_1"
+ : "iqs269a_slider_0";
+ iqs269->slider[i]->id.bustype = BUS_I2C;
+
+ for (j = 0; j < IQS269_NUM_GESTURES; j++)
+ if (iqs269->sl_code[i][j] != KEY_RESERVED)
+ input_set_capability(iqs269->slider[i], EV_KEY,
+ iqs269->sl_code[i][j]);
+
+ /*
+ * Present the slider as a narrow trackpad if one or more chan-
+ * nels have been selected to participate, but no gestures have
+ * been mapped to a keycode.
+ */
+ if (iqs269_slider_type(iqs269, i) == IQS269_SLIDER_RAW) {
+ input_set_capability(iqs269->slider[i],
+ EV_KEY, BTN_TOUCH);
+ input_set_abs_params(iqs269->slider[i],
+ ABS_X, 0, 255, 0, 0);
+ }
+
+ error = input_register_device(iqs269->slider[i]);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register slider %d: %d\n", i, error);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static int iqs269_report(struct iqs269_private *iqs269)
+{
+ struct i2c_client *client = iqs269->client;
+ struct iqs269_flags flags;
+ unsigned int sw_code, keycode;
+ int error, i, j;
+ u8 slider_x[IQS269_NUM_SL];
+ u8 dir_mask, state;
+
+ error = regmap_raw_read(iqs269->regmap, IQS269_SYS_FLAGS, &flags,
+ sizeof(flags));
+ if (error) {
+ dev_err(&client->dev, "Failed to read device status: %d\n",
+ error);
+ return error;
+ }
+
+ /*
+ * The device resets itself if its own watchdog bites, which can happen
+ * in the event of an I2C communication error. In this case, the device
+ * asserts a SHOW_RESET interrupt and all registers must be restored.
+ */
+ if (be16_to_cpu(flags.system) & IQS269_SYS_FLAGS_SHOW_RESET) {
+ dev_err(&client->dev, "Unexpected device reset\n");
+
+ error = iqs269_dev_init(iqs269);
+ if (error)
+ dev_err(&client->dev,
+ "Failed to re-initialize device: %d\n", error);
+
+ return error;
+ }
+
+ if (be16_to_cpu(flags.system) & IQS269_SYS_FLAGS_IN_ATI)
+ return 0;
+
+ if (iqs269_slider_type(iqs269, 0) == IQS269_SLIDER_RAW ||
+ iqs269_slider_type(iqs269, 1) == IQS269_SLIDER_RAW) {
+ error = regmap_raw_read(iqs269->regmap, IQS269_SLIDER_X,
+ slider_x, sizeof(slider_x));
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read slider position: %d\n", error);
+ return error;
+ }
+ }
+
+ for (i = 0; i < IQS269_NUM_SL; i++) {
+ flags.gesture >>= (i * IQS269_NUM_GESTURES);
+
+ switch (iqs269_slider_type(iqs269, i)) {
+ case IQS269_SLIDER_NONE:
+ continue;
+
+ case IQS269_SLIDER_KEY:
+ for (j = 0; j < IQS269_NUM_GESTURES; j++)
+ input_report_key(iqs269->slider[i],
+ iqs269->sl_code[i][j],
+ flags.gesture & BIT(j));
+
+ if (!(flags.gesture & (BIT(IQS269_GESTURE_FLICK_NEG) |
+ BIT(IQS269_GESTURE_FLICK_POS) |
+ BIT(IQS269_GESTURE_TAP))))
+ break;
+
+ input_sync(iqs269->slider[i]);
+
+ /*
+ * Momentary gestures are followed by a complementary
+ * release cycle so as to emulate a full keystroke.
+ */
+ for (j = 0; j < IQS269_NUM_GESTURES; j++)
+ if (j != IQS269_GESTURE_HOLD)
+ input_report_key(iqs269->slider[i],
+ iqs269->sl_code[i][j],
+ 0);
+ break;
+
+ case IQS269_SLIDER_RAW:
+ /*
+ * The slider is considered to be in a state of touch
+ * if any selected channels are in a state of touch.
+ */
+ state = flags.states[IQS269_ST_OFFS_TOUCH];
+ state &= iqs269->sys_reg.slider_select[i];
+
+ input_report_key(iqs269->slider[i], BTN_TOUCH, state);
+
+ if (state)
+ input_report_abs(iqs269->slider[i],
+ ABS_X, slider_x[i]);
+ break;
+ }
+
+ input_sync(iqs269->slider[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+ dir_mask = flags.states[IQS269_ST_OFFS_DIR];
+ if (!iqs269_events[i].dir_up)
+ dir_mask = ~dir_mask;
+
+ state = flags.states[iqs269_events[i].st_offs] & dir_mask;
+
+ sw_code = iqs269->switches[i].code;
+
+ for (j = 0; j < IQS269_NUM_CH; j++) {
+ keycode = iqs269->keycode[i * IQS269_NUM_CH + j];
+
+ switch (j) {
+ case IQS269_CHx_HALL_ACTIVE:
+ if (iqs269->hall_enable &&
+ iqs269->switches[i].enabled)
+ input_report_switch(iqs269->keypad,
+ sw_code,
+ state & BIT(j));
+ fallthrough;
+
+ case IQS269_CHx_HALL_INACTIVE:
+ if (iqs269->hall_enable)
+ continue;
+ fallthrough;
+
+ default:
+ input_report_key(iqs269->keypad, keycode,
+ state & BIT(j));
+ }
+ }
+ }
+
+ input_sync(iqs269->keypad);
+
+ /*
+ * The following completion signals that ATI has finished, any initial
+ * switch states have been reported and the keypad can be registered.
+ */
+ complete_all(&iqs269->ati_done);
+
+ return 0;
+}
+
+static irqreturn_t iqs269_irq(int irq, void *context)
+{
+ struct iqs269_private *iqs269 = context;
+
+ if (iqs269_report(iqs269))
+ return IRQ_NONE;
+
+ /*
+ * The device does not deassert its interrupt (RDY) pin until shortly
+ * after receiving an I2C stop condition; the following delay ensures
+ * the interrupt handler does not return before this time.
+ */
+ iqs269_irq_wait();
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t counts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs269->client;
+ __le16 counts;
+ int error;
+
+ if (!iqs269->ati_current || iqs269->hall_enable)
+ return -EPERM;
+
+ if (!completion_done(&iqs269->ati_done))
+ return -EBUSY;
+
+ /*
+ * Unsolicited I2C communication prompts the device to assert its RDY
+ * pin, so disable the interrupt line until the operation is finished
+ * and RDY has been deasserted.
+ */
+ disable_irq(client->irq);
+
+ error = regmap_raw_read(iqs269->regmap,
+ IQS269_CHx_COUNTS + iqs269->ch_num * 2,
+ &counts, sizeof(counts));
+
+ iqs269_irq_wait();
+ enable_irq(client->irq);
+
+ if (error)
+ return error;
+
+ return sysfs_emit(buf, "%u\n", le16_to_cpu(counts));
+}
+
+static ssize_t hall_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ struct i2c_client *client = iqs269->client;
+ unsigned int val;
+ int error;
+
+ disable_irq(client->irq);
+
+ error = regmap_read(iqs269->regmap, IQS269_CAL_DATA_A, &val);
+
+ iqs269_irq_wait();
+ enable_irq(client->irq);
+
+ if (error)
+ return error;
+
+ switch (ch_reg[IQS269_CHx_HALL_ACTIVE].rx_enable &
+ ch_reg[IQS269_CHx_HALL_INACTIVE].rx_enable) {
+ case IQS269_HALL_PAD_R:
+ val &= IQS269_CAL_DATA_A_HALL_BIN_R_MASK;
+ val >>= IQS269_CAL_DATA_A_HALL_BIN_R_SHIFT;
+ break;
+
+ case IQS269_HALL_PAD_L:
+ val &= IQS269_CAL_DATA_A_HALL_BIN_L_MASK;
+ val >>= IQS269_CAL_DATA_A_HALL_BIN_L_SHIFT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t hall_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", iqs269->hall_enable);
+}
+
+static ssize_t hall_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ guard(mutex)(&iqs269->lock);
+
+ iqs269->hall_enable = val;
+ iqs269->ati_current = false;
+
+ return count;
+}
+
+static ssize_t ch_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", iqs269->ch_num);
+}
+
+static ssize_t ch_number_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ if (val >= IQS269_NUM_CH)
+ return -EINVAL;
+
+ iqs269->ch_num = val;
+
+ return count;
+}
+
+static ssize_t rx_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+
+ return sysfs_emit(buf, "%u\n", ch_reg[iqs269->ch_num].rx_enable);
+}
+
+static ssize_t rx_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg;
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ if (val > 0xFF)
+ return -EINVAL;
+
+ guard(mutex)(&iqs269->lock);
+
+ ch_reg[iqs269->ch_num].rx_enable = val;
+ iqs269->ati_current = false;
+
+ return count;
+}
+
+static ssize_t ati_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = iqs269_ati_mode_get(iqs269, iqs269->ch_num, &val);
+ if (error)
+ return error;
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t ati_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ error = iqs269_ati_mode_set(iqs269, iqs269->ch_num, val);
+ if (error)
+ return error;
+
+ return count;
+}
+
+static ssize_t ati_base_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = iqs269_ati_base_get(iqs269, iqs269->ch_num, &val);
+ if (error)
+ return error;
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t ati_base_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ error = iqs269_ati_base_set(iqs269, iqs269->ch_num, val);
+ if (error)
+ return error;
+
+ return count;
+}
+
+static ssize_t ati_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = iqs269_ati_target_get(iqs269, iqs269->ch_num, &val);
+ if (error)
+ return error;
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t ati_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ error = iqs269_ati_target_set(iqs269, iqs269->ch_num, val);
+ if (error)
+ return error;
+
+ return count;
+}
+
+static ssize_t ati_trigger_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n",
+ iqs269->ati_current &&
+ completion_done(&iqs269->ati_done));
+}
+
+static ssize_t ati_trigger_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs269->client;
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ if (!val)
+ return count;
+
+ disable_irq(client->irq);
+ reinit_completion(&iqs269->ati_done);
+
+ error = iqs269_dev_init(iqs269);
+
+ iqs269_irq_wait();
+ enable_irq(client->irq);
+
+ if (error)
+ return error;
+
+ if (!wait_for_completion_timeout(&iqs269->ati_done,
+ msecs_to_jiffies(2000)))
+ return -ETIMEDOUT;
+
+ return count;
+}
+
+static DEVICE_ATTR_RO(counts);
+static DEVICE_ATTR_RO(hall_bin);
+static DEVICE_ATTR_RW(hall_enable);
+static DEVICE_ATTR_RW(ch_number);
+static DEVICE_ATTR_RW(rx_enable);
+static DEVICE_ATTR_RW(ati_mode);
+static DEVICE_ATTR_RW(ati_base);
+static DEVICE_ATTR_RW(ati_target);
+static DEVICE_ATTR_RW(ati_trigger);
+
+static struct attribute *iqs269_attrs[] = {
+ &dev_attr_counts.attr,
+ &dev_attr_hall_bin.attr,
+ &dev_attr_hall_enable.attr,
+ &dev_attr_ch_number.attr,
+ &dev_attr_rx_enable.attr,
+ &dev_attr_ati_mode.attr,
+ &dev_attr_ati_base.attr,
+ &dev_attr_ati_target.attr,
+ &dev_attr_ati_trigger.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(iqs269);
+
+static const struct regmap_config iqs269_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = IQS269_MAX_REG,
+};
+
+static int iqs269_probe(struct i2c_client *client)
+{
+ struct iqs269_private *iqs269;
+ int error;
+
+ iqs269 = devm_kzalloc(&client->dev, sizeof(*iqs269), GFP_KERNEL);
+ if (!iqs269)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, iqs269);
+ iqs269->client = client;
+
+ iqs269->regmap = devm_regmap_init_i2c(client, &iqs269_regmap_config);
+ if (IS_ERR(iqs269->regmap)) {
+ error = PTR_ERR(iqs269->regmap);
+ dev_err(&client->dev, "Failed to initialize register map: %d\n",
+ error);
+ return error;
+ }
+
+ mutex_init(&iqs269->lock);
+ init_completion(&iqs269->ati_done);
+
+ iqs269->otp_option = (uintptr_t)device_get_match_data(&client->dev);
+
+ error = regmap_raw_read(iqs269->regmap, IQS269_VER_INFO,
+ &iqs269->ver_info, sizeof(iqs269->ver_info));
+ if (error)
+ return error;
+
+ if (iqs269->ver_info.prod_num != IQS269_VER_INFO_PROD_NUM) {
+ dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
+ iqs269->ver_info.prod_num);
+ return -EINVAL;
+ }
+
+ error = iqs269_parse_prop(iqs269);
+ if (error)
+ return error;
+
+ error = iqs269_dev_init(iqs269);
+ if (error) {
+ dev_err(&client->dev, "Failed to initialize device: %d\n",
+ error);
+ return error;
+ }
+
+ error = iqs269_input_init(iqs269);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, iqs269_irq, IRQF_ONESHOT,
+ client->name, iqs269);
+ if (error) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+ return error;
+ }
+
+ if (!wait_for_completion_timeout(&iqs269->ati_done,
+ msecs_to_jiffies(2000))) {
+ dev_err(&client->dev, "Failed to complete ATI\n");
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * The keypad may include one or more switches and is not registered
+ * until ATI is complete and the initial switch states are read.
+ */
+ error = input_register_device(iqs269->keypad);
+ if (error) {
+ dev_err(&client->dev, "Failed to register keypad: %d\n", error);
+ return error;
+ }
+
+ return error;
+}
+
+static u16 iqs269_general_get(struct iqs269_private *iqs269)
+{
+ u16 general = be16_to_cpu(iqs269->sys_reg.general);
+
+ general &= ~IQS269_SYS_SETTINGS_REDO_ATI;
+ general &= ~IQS269_SYS_SETTINGS_ACK_RESET;
+
+ return general | IQS269_SYS_SETTINGS_DIS_AUTO;
+}
+
+static int iqs269_suspend(struct device *dev)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs269->client;
+ int error;
+ u16 general = iqs269_general_get(iqs269);
+
+ if (!(general & IQS269_SYS_SETTINGS_PWR_MODE_MASK))
+ return 0;
+
+ disable_irq(client->irq);
+
+ error = regmap_write(iqs269->regmap, IQS269_SYS_SETTINGS, general);
+
+ iqs269_irq_wait();
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static int iqs269_resume(struct device *dev)
+{
+ struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs269->client;
+ int error;
+ u16 general = iqs269_general_get(iqs269);
+
+ if (!(general & IQS269_SYS_SETTINGS_PWR_MODE_MASK))
+ return 0;
+
+ disable_irq(client->irq);
+
+ error = regmap_write(iqs269->regmap, IQS269_SYS_SETTINGS,
+ general & ~IQS269_SYS_SETTINGS_PWR_MODE_MASK);
+ if (!error)
+ error = regmap_write(iqs269->regmap, IQS269_SYS_SETTINGS,
+ general & ~IQS269_SYS_SETTINGS_DIS_AUTO);
+
+ iqs269_irq_wait();
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(iqs269_pm, iqs269_suspend, iqs269_resume);
+
+static const struct of_device_id iqs269_of_match[] = {
+ {
+ .compatible = "azoteq,iqs269a",
+ .data = (void *)IQS269_OTP_OPTION_DEFAULT,
+ },
+ {
+ .compatible = "azoteq,iqs269a-00",
+ .data = (void *)IQS269_OTP_OPTION_DEFAULT,
+ },
+ {
+ .compatible = "azoteq,iqs269a-d0",
+ .data = (void *)IQS269_OTP_OPTION_TWS,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, iqs269_of_match);
+
+static struct i2c_driver iqs269_i2c_driver = {
+ .driver = {
+ .name = "iqs269a",
+ .dev_groups = iqs269_groups,
+ .of_match_table = iqs269_of_match,
+ .pm = pm_sleep_ptr(&iqs269_pm),
+ },
+ .probe = iqs269_probe,
+};
+module_i2c_driver(iqs269_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS269A Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/iqs626a.c b/drivers/input/misc/iqs626a.c
new file mode 100644
index 000000000000..7fba4a8edceb
--- /dev/null
+++ b/drivers/input/misc/iqs626a.c
@@ -0,0 +1,1821 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS626A Capacitive Touch Controller
+ *
+ * Copyright (C) 2020 Jeff LaBundy <jeff@labundy.com>
+ *
+ * This driver registers up to 2 input devices: one representing capacitive or
+ * inductive keys as well as Hall-effect switches, and one for a trackpad that
+ * can express various gestures.
+ */
+
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS626_VER_INFO 0x00
+#define IQS626_VER_INFO_PROD_NUM 0x51
+
+#define IQS626_SYS_FLAGS 0x02
+#define IQS626_SYS_FLAGS_SHOW_RESET BIT(15)
+#define IQS626_SYS_FLAGS_IN_ATI BIT(12)
+#define IQS626_SYS_FLAGS_PWR_MODE_MASK GENMASK(9, 8)
+#define IQS626_SYS_FLAGS_PWR_MODE_SHIFT 8
+
+#define IQS626_HALL_OUTPUT 0x23
+
+#define IQS626_SYS_SETTINGS 0x80
+#define IQS626_SYS_SETTINGS_CLK_DIV BIT(15)
+#define IQS626_SYS_SETTINGS_ULP_AUTO BIT(14)
+#define IQS626_SYS_SETTINGS_DIS_AUTO BIT(13)
+#define IQS626_SYS_SETTINGS_PWR_MODE_MASK GENMASK(12, 11)
+#define IQS626_SYS_SETTINGS_PWR_MODE_SHIFT 11
+#define IQS626_SYS_SETTINGS_PWR_MODE_MAX 3
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_MASK GENMASK(10, 8)
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT 8
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_MAX 7
+#define IQS626_SYS_SETTINGS_EVENT_MODE BIT(5)
+#define IQS626_SYS_SETTINGS_EVENT_MODE_LP BIT(4)
+#define IQS626_SYS_SETTINGS_REDO_ATI BIT(2)
+#define IQS626_SYS_SETTINGS_ACK_RESET BIT(0)
+
+#define IQS626_MISC_A_ATI_BAND_DISABLE BIT(7)
+#define IQS626_MISC_A_TPx_LTA_UPDATE_MASK GENMASK(6, 4)
+#define IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT 4
+#define IQS626_MISC_A_TPx_LTA_UPDATE_MAX 7
+#define IQS626_MISC_A_ATI_LP_ONLY BIT(3)
+#define IQS626_MISC_A_GPIO3_SELECT_MASK GENMASK(2, 0)
+#define IQS626_MISC_A_GPIO3_SELECT_MAX 7
+
+#define IQS626_EVENT_MASK_SYS BIT(6)
+#define IQS626_EVENT_MASK_GESTURE BIT(3)
+#define IQS626_EVENT_MASK_DEEP BIT(2)
+#define IQS626_EVENT_MASK_TOUCH BIT(1)
+#define IQS626_EVENT_MASK_PROX BIT(0)
+
+#define IQS626_RATE_NP_MS_MAX 255
+#define IQS626_RATE_LP_MS_MAX 255
+#define IQS626_RATE_ULP_MS_MAX 4080
+#define IQS626_TIMEOUT_PWR_MS_MAX 130560
+#define IQS626_TIMEOUT_LTA_MS_MAX 130560
+
+#define IQS626_MISC_B_RESEED_UI_SEL_MASK GENMASK(7, 6)
+#define IQS626_MISC_B_RESEED_UI_SEL_SHIFT 6
+#define IQS626_MISC_B_RESEED_UI_SEL_MAX 3
+#define IQS626_MISC_B_THRESH_EXTEND BIT(5)
+#define IQS626_MISC_B_TRACKING_UI_ENABLE BIT(4)
+#define IQS626_MISC_B_TPx_SWIPE BIT(3)
+#define IQS626_MISC_B_RESEED_OFFSET BIT(2)
+#define IQS626_MISC_B_FILT_STR_TPx GENMASK(1, 0)
+
+#define IQS626_THRESH_SWIPE_MAX 255
+#define IQS626_TIMEOUT_TAP_MS_MAX 4080
+#define IQS626_TIMEOUT_SWIPE_MS_MAX 4080
+
+#define IQS626_CHx_ENG_0_MEAS_CAP_SIZE BIT(7)
+#define IQS626_CHx_ENG_0_RX_TERM_VSS BIT(5)
+#define IQS626_CHx_ENG_0_LINEARIZE BIT(4)
+#define IQS626_CHx_ENG_0_DUAL_DIR BIT(3)
+#define IQS626_CHx_ENG_0_FILT_DISABLE BIT(2)
+#define IQS626_CHx_ENG_0_ATI_MODE_MASK GENMASK(1, 0)
+#define IQS626_CHx_ENG_0_ATI_MODE_MAX 3
+
+#define IQS626_CHx_ENG_1_CCT_HIGH_1 BIT(7)
+#define IQS626_CHx_ENG_1_CCT_HIGH_0 BIT(6)
+#define IQS626_CHx_ENG_1_PROJ_BIAS_MASK GENMASK(5, 4)
+#define IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT 4
+#define IQS626_CHx_ENG_1_PROJ_BIAS_MAX 3
+#define IQS626_CHx_ENG_1_CCT_ENABLE BIT(3)
+#define IQS626_CHx_ENG_1_SENSE_FREQ_MASK GENMASK(2, 1)
+#define IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT 1
+#define IQS626_CHx_ENG_1_SENSE_FREQ_MAX 3
+#define IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN BIT(0)
+
+#define IQS626_CHx_ENG_2_LOCAL_CAP_MASK GENMASK(7, 6)
+#define IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT 6
+#define IQS626_CHx_ENG_2_LOCAL_CAP_MAX 3
+#define IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE BIT(5)
+#define IQS626_CHx_ENG_2_SENSE_MODE_MASK GENMASK(3, 0)
+#define IQS626_CHx_ENG_2_SENSE_MODE_MAX 15
+
+#define IQS626_CHx_ENG_3_TX_FREQ_MASK GENMASK(5, 4)
+#define IQS626_CHx_ENG_3_TX_FREQ_SHIFT 4
+#define IQS626_CHx_ENG_3_TX_FREQ_MAX 3
+#define IQS626_CHx_ENG_3_INV_LOGIC BIT(0)
+
+#define IQS626_CHx_ENG_4_RX_TERM_VREG BIT(6)
+#define IQS626_CHx_ENG_4_CCT_LOW_1 BIT(5)
+#define IQS626_CHx_ENG_4_CCT_LOW_0 BIT(4)
+#define IQS626_CHx_ENG_4_COMP_DISABLE BIT(1)
+#define IQS626_CHx_ENG_4_STATIC_ENABLE BIT(0)
+
+#define IQS626_TPx_ATI_BASE_MIN 45
+#define IQS626_TPx_ATI_BASE_MAX 300
+#define IQS626_CHx_ATI_BASE_MASK GENMASK(7, 6)
+#define IQS626_CHx_ATI_BASE_75 0x00
+#define IQS626_CHx_ATI_BASE_100 0x40
+#define IQS626_CHx_ATI_BASE_150 0x80
+#define IQS626_CHx_ATI_BASE_200 0xC0
+#define IQS626_CHx_ATI_TARGET_MASK GENMASK(5, 0)
+#define IQS626_CHx_ATI_TARGET_MAX 2016
+
+#define IQS626_CHx_THRESH_MAX 255
+#define IQS626_CHx_HYST_DEEP_MASK GENMASK(7, 4)
+#define IQS626_CHx_HYST_DEEP_SHIFT 4
+#define IQS626_CHx_HYST_TOUCH_MASK GENMASK(3, 0)
+#define IQS626_CHx_HYST_MAX 15
+
+#define IQS626_FILT_STR_NP_TPx_MASK GENMASK(7, 6)
+#define IQS626_FILT_STR_NP_TPx_SHIFT 6
+#define IQS626_FILT_STR_LP_TPx_MASK GENMASK(5, 4)
+#define IQS626_FILT_STR_LP_TPx_SHIFT 4
+
+#define IQS626_FILT_STR_NP_CNT_MASK GENMASK(7, 6)
+#define IQS626_FILT_STR_NP_CNT_SHIFT 6
+#define IQS626_FILT_STR_LP_CNT_MASK GENMASK(5, 4)
+#define IQS626_FILT_STR_LP_CNT_SHIFT 4
+#define IQS626_FILT_STR_NP_LTA_MASK GENMASK(3, 2)
+#define IQS626_FILT_STR_NP_LTA_SHIFT 2
+#define IQS626_FILT_STR_LP_LTA_MASK GENMASK(1, 0)
+#define IQS626_FILT_STR_MAX 3
+
+#define IQS626_ULP_PROJ_ENABLE BIT(4)
+#define IQS626_GEN_WEIGHT_MAX 255
+
+#define IQS626_MAX_REG 0xFF
+
+#define IQS626_NUM_CH_TP_3 9
+#define IQS626_NUM_CH_TP_2 6
+#define IQS626_NUM_CH_GEN 3
+#define IQS626_NUM_CRx_TX 8
+
+#define IQS626_PWR_MODE_POLL_SLEEP_US 50000
+#define IQS626_PWR_MODE_POLL_TIMEOUT_US 500000
+
+#define iqs626_irq_wait() usleep_range(350, 400)
+
+enum iqs626_ch_id {
+ IQS626_CH_ULP_0,
+ IQS626_CH_TP_2,
+ IQS626_CH_TP_3,
+ IQS626_CH_GEN_0,
+ IQS626_CH_GEN_1,
+ IQS626_CH_GEN_2,
+ IQS626_CH_HALL,
+};
+
+enum iqs626_rx_inactive {
+ IQS626_RX_INACTIVE_VSS,
+ IQS626_RX_INACTIVE_FLOAT,
+ IQS626_RX_INACTIVE_VREG,
+};
+
+enum iqs626_st_offs {
+ IQS626_ST_OFFS_PROX,
+ IQS626_ST_OFFS_DIR,
+ IQS626_ST_OFFS_TOUCH,
+ IQS626_ST_OFFS_DEEP,
+};
+
+enum iqs626_th_offs {
+ IQS626_TH_OFFS_PROX,
+ IQS626_TH_OFFS_TOUCH,
+ IQS626_TH_OFFS_DEEP,
+};
+
+enum iqs626_event_id {
+ IQS626_EVENT_PROX_DN,
+ IQS626_EVENT_PROX_UP,
+ IQS626_EVENT_TOUCH_DN,
+ IQS626_EVENT_TOUCH_UP,
+ IQS626_EVENT_DEEP_DN,
+ IQS626_EVENT_DEEP_UP,
+};
+
+enum iqs626_gesture_id {
+ IQS626_GESTURE_FLICK_X_POS,
+ IQS626_GESTURE_FLICK_X_NEG,
+ IQS626_GESTURE_FLICK_Y_POS,
+ IQS626_GESTURE_FLICK_Y_NEG,
+ IQS626_GESTURE_TAP,
+ IQS626_GESTURE_HOLD,
+ IQS626_NUM_GESTURES,
+};
+
+struct iqs626_event_desc {
+ const char *name;
+ enum iqs626_st_offs st_offs;
+ enum iqs626_th_offs th_offs;
+ bool dir_up;
+ u8 mask;
+};
+
+static const struct iqs626_event_desc iqs626_events[] = {
+ [IQS626_EVENT_PROX_DN] = {
+ .name = "event-prox",
+ .st_offs = IQS626_ST_OFFS_PROX,
+ .th_offs = IQS626_TH_OFFS_PROX,
+ .mask = IQS626_EVENT_MASK_PROX,
+ },
+ [IQS626_EVENT_PROX_UP] = {
+ .name = "event-prox-alt",
+ .st_offs = IQS626_ST_OFFS_PROX,
+ .th_offs = IQS626_TH_OFFS_PROX,
+ .dir_up = true,
+ .mask = IQS626_EVENT_MASK_PROX,
+ },
+ [IQS626_EVENT_TOUCH_DN] = {
+ .name = "event-touch",
+ .st_offs = IQS626_ST_OFFS_TOUCH,
+ .th_offs = IQS626_TH_OFFS_TOUCH,
+ .mask = IQS626_EVENT_MASK_TOUCH,
+ },
+ [IQS626_EVENT_TOUCH_UP] = {
+ .name = "event-touch-alt",
+ .st_offs = IQS626_ST_OFFS_TOUCH,
+ .th_offs = IQS626_TH_OFFS_TOUCH,
+ .dir_up = true,
+ .mask = IQS626_EVENT_MASK_TOUCH,
+ },
+ [IQS626_EVENT_DEEP_DN] = {
+ .name = "event-deep",
+ .st_offs = IQS626_ST_OFFS_DEEP,
+ .th_offs = IQS626_TH_OFFS_DEEP,
+ .mask = IQS626_EVENT_MASK_DEEP,
+ },
+ [IQS626_EVENT_DEEP_UP] = {
+ .name = "event-deep-alt",
+ .st_offs = IQS626_ST_OFFS_DEEP,
+ .th_offs = IQS626_TH_OFFS_DEEP,
+ .dir_up = true,
+ .mask = IQS626_EVENT_MASK_DEEP,
+ },
+};
+
+struct iqs626_ver_info {
+ u8 prod_num;
+ u8 sw_num;
+ u8 hw_num;
+ u8 padding;
+} __packed;
+
+struct iqs626_flags {
+ __be16 system;
+ u8 gesture;
+ u8 padding_a;
+ u8 states[4];
+ u8 ref_active;
+ u8 padding_b;
+ u8 comp_min;
+ u8 comp_max;
+ u8 trackpad_x;
+ u8 trackpad_y;
+} __packed;
+
+struct iqs626_ch_reg_ulp {
+ u8 thresh[2];
+ u8 hyst;
+ u8 filter;
+ u8 engine[2];
+ u8 ati_target;
+ u8 padding;
+ __be16 ati_comp;
+ u8 rx_enable;
+ u8 tx_enable;
+} __packed;
+
+struct iqs626_ch_reg_tp {
+ u8 thresh;
+ u8 ati_base;
+ __be16 ati_comp;
+} __packed;
+
+struct iqs626_tp_grp_reg {
+ u8 hyst;
+ u8 ati_target;
+ u8 engine[2];
+ struct iqs626_ch_reg_tp ch_reg_tp[IQS626_NUM_CH_TP_3];
+} __packed;
+
+struct iqs626_ch_reg_gen {
+ u8 thresh[3];
+ u8 padding;
+ u8 hyst;
+ u8 ati_target;
+ __be16 ati_comp;
+ u8 engine[5];
+ u8 filter;
+ u8 rx_enable;
+ u8 tx_enable;
+ u8 assoc_select;
+ u8 assoc_weight;
+} __packed;
+
+struct iqs626_ch_reg_hall {
+ u8 engine;
+ u8 thresh;
+ u8 hyst;
+ u8 ati_target;
+ __be16 ati_comp;
+} __packed;
+
+struct iqs626_sys_reg {
+ __be16 general;
+ u8 misc_a;
+ u8 event_mask;
+ u8 active;
+ u8 reseed;
+ u8 rate_np;
+ u8 rate_lp;
+ u8 rate_ulp;
+ u8 timeout_pwr;
+ u8 timeout_rdy;
+ u8 timeout_lta;
+ u8 misc_b;
+ u8 thresh_swipe;
+ u8 timeout_tap;
+ u8 timeout_swipe;
+ u8 redo_ati;
+ u8 padding;
+ struct iqs626_ch_reg_ulp ch_reg_ulp;
+ struct iqs626_tp_grp_reg tp_grp_reg;
+ struct iqs626_ch_reg_gen ch_reg_gen[IQS626_NUM_CH_GEN];
+ struct iqs626_ch_reg_hall ch_reg_hall;
+} __packed;
+
+struct iqs626_channel_desc {
+ const char *name;
+ int num_ch;
+ u8 active;
+ bool events[ARRAY_SIZE(iqs626_events)];
+};
+
+static const struct iqs626_channel_desc iqs626_channels[] = {
+ [IQS626_CH_ULP_0] = {
+ .name = "ulp-0",
+ .num_ch = 1,
+ .active = BIT(0),
+ .events = {
+ [IQS626_EVENT_PROX_DN] = true,
+ [IQS626_EVENT_PROX_UP] = true,
+ [IQS626_EVENT_TOUCH_DN] = true,
+ [IQS626_EVENT_TOUCH_UP] = true,
+ },
+ },
+ [IQS626_CH_TP_2] = {
+ .name = "trackpad-3x2",
+ .num_ch = IQS626_NUM_CH_TP_2,
+ .active = BIT(1),
+ .events = {
+ [IQS626_EVENT_TOUCH_DN] = true,
+ },
+ },
+ [IQS626_CH_TP_3] = {
+ .name = "trackpad-3x3",
+ .num_ch = IQS626_NUM_CH_TP_3,
+ .active = BIT(2) | BIT(1),
+ .events = {
+ [IQS626_EVENT_TOUCH_DN] = true,
+ },
+ },
+ [IQS626_CH_GEN_0] = {
+ .name = "generic-0",
+ .num_ch = 1,
+ .active = BIT(4),
+ .events = {
+ [IQS626_EVENT_PROX_DN] = true,
+ [IQS626_EVENT_PROX_UP] = true,
+ [IQS626_EVENT_TOUCH_DN] = true,
+ [IQS626_EVENT_TOUCH_UP] = true,
+ [IQS626_EVENT_DEEP_DN] = true,
+ [IQS626_EVENT_DEEP_UP] = true,
+ },
+ },
+ [IQS626_CH_GEN_1] = {
+ .name = "generic-1",
+ .num_ch = 1,
+ .active = BIT(5),
+ .events = {
+ [IQS626_EVENT_PROX_DN] = true,
+ [IQS626_EVENT_PROX_UP] = true,
+ [IQS626_EVENT_TOUCH_DN] = true,
+ [IQS626_EVENT_TOUCH_UP] = true,
+ [IQS626_EVENT_DEEP_DN] = true,
+ [IQS626_EVENT_DEEP_UP] = true,
+ },
+ },
+ [IQS626_CH_GEN_2] = {
+ .name = "generic-2",
+ .num_ch = 1,
+ .active = BIT(6),
+ .events = {
+ [IQS626_EVENT_PROX_DN] = true,
+ [IQS626_EVENT_PROX_UP] = true,
+ [IQS626_EVENT_TOUCH_DN] = true,
+ [IQS626_EVENT_TOUCH_UP] = true,
+ [IQS626_EVENT_DEEP_DN] = true,
+ [IQS626_EVENT_DEEP_UP] = true,
+ },
+ },
+ [IQS626_CH_HALL] = {
+ .name = "hall",
+ .num_ch = 1,
+ .active = BIT(7),
+ .events = {
+ [IQS626_EVENT_TOUCH_DN] = true,
+ [IQS626_EVENT_TOUCH_UP] = true,
+ },
+ },
+};
+
+struct iqs626_private {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct iqs626_sys_reg sys_reg;
+ struct completion ati_done;
+ struct input_dev *keypad;
+ struct input_dev *trackpad;
+ struct touchscreen_properties prop;
+ unsigned int kp_type[ARRAY_SIZE(iqs626_channels)]
+ [ARRAY_SIZE(iqs626_events)];
+ unsigned int kp_code[ARRAY_SIZE(iqs626_channels)]
+ [ARRAY_SIZE(iqs626_events)];
+ unsigned int tp_code[IQS626_NUM_GESTURES];
+ unsigned int suspend_mode;
+};
+
+static noinline_for_stack int
+iqs626_parse_events(struct iqs626_private *iqs626,
+ struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ const char *ev_name;
+ u8 *thresh, *hyst;
+ unsigned int val;
+ int i;
+
+ switch (ch_id) {
+ case IQS626_CH_ULP_0:
+ thresh = sys_reg->ch_reg_ulp.thresh;
+ hyst = &sys_reg->ch_reg_ulp.hyst;
+ break;
+
+ case IQS626_CH_TP_2:
+ case IQS626_CH_TP_3:
+ thresh = &sys_reg->tp_grp_reg.ch_reg_tp[0].thresh;
+ hyst = &sys_reg->tp_grp_reg.hyst;
+ break;
+
+ case IQS626_CH_GEN_0:
+ case IQS626_CH_GEN_1:
+ case IQS626_CH_GEN_2:
+ i = ch_id - IQS626_CH_GEN_0;
+ thresh = sys_reg->ch_reg_gen[i].thresh;
+ hyst = &sys_reg->ch_reg_gen[i].hyst;
+ break;
+
+ case IQS626_CH_HALL:
+ thresh = &sys_reg->ch_reg_hall.thresh;
+ hyst = &sys_reg->ch_reg_hall.hyst;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(iqs626_events); i++) {
+ if (!iqs626_channels[ch_id].events[i])
+ continue;
+
+ struct fwnode_handle *ev_node __free(fwnode_handle) = NULL;
+ if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3) {
+ /*
+ * Trackpad touch events are simply described under the
+ * trackpad child node.
+ */
+ ev_node = fwnode_handle_get(ch_node);
+ } else {
+ ev_name = iqs626_events[i].name;
+ ev_node = fwnode_get_named_child_node(ch_node, ev_name);
+ if (!ev_node)
+ continue;
+
+ if (!fwnode_property_read_u32(ev_node, "linux,code",
+ &val)) {
+ iqs626->kp_code[ch_id][i] = val;
+
+ if (fwnode_property_read_u32(ev_node,
+ "linux,input-type",
+ &val)) {
+ if (ch_id == IQS626_CH_HALL)
+ val = EV_SW;
+ else
+ val = EV_KEY;
+ }
+
+ if (val != EV_KEY && val != EV_SW) {
+ dev_err(&client->dev,
+ "Invalid input type: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ iqs626->kp_type[ch_id][i] = val;
+
+ sys_reg->event_mask &= ~iqs626_events[i].mask;
+ }
+ }
+
+ if (!fwnode_property_read_u32(ev_node, "azoteq,hyst", &val)) {
+ if (val > IQS626_CHx_HYST_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel hysteresis: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ if (i == IQS626_EVENT_DEEP_DN ||
+ i == IQS626_EVENT_DEEP_UP) {
+ *hyst &= ~IQS626_CHx_HYST_DEEP_MASK;
+ *hyst |= (val << IQS626_CHx_HYST_DEEP_SHIFT);
+ } else if (i == IQS626_EVENT_TOUCH_DN ||
+ i == IQS626_EVENT_TOUCH_UP) {
+ *hyst &= ~IQS626_CHx_HYST_TOUCH_MASK;
+ *hyst |= val;
+ }
+ }
+
+ if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
+ !fwnode_property_read_u32(ev_node, "azoteq,thresh", &val)) {
+ if (val > IQS626_CHx_THRESH_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel threshold: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ if (ch_id == IQS626_CH_HALL)
+ *thresh = val;
+ else
+ *(thresh + iqs626_events[i].th_offs) = val;
+ }
+ }
+
+ return 0;
+}
+
+static noinline_for_stack int
+iqs626_parse_ati_target(struct iqs626_private *iqs626,
+ struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ unsigned int val;
+ u8 *ati_target;
+ int i;
+
+ switch (ch_id) {
+ case IQS626_CH_ULP_0:
+ ati_target = &sys_reg->ch_reg_ulp.ati_target;
+ break;
+
+ case IQS626_CH_TP_2:
+ case IQS626_CH_TP_3:
+ ati_target = &sys_reg->tp_grp_reg.ati_target;
+ break;
+
+ case IQS626_CH_GEN_0:
+ case IQS626_CH_GEN_1:
+ case IQS626_CH_GEN_2:
+ i = ch_id - IQS626_CH_GEN_0;
+ ati_target = &sys_reg->ch_reg_gen[i].ati_target;
+ break;
+
+ case IQS626_CH_HALL:
+ ati_target = &sys_reg->ch_reg_hall.ati_target;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,ati-target", &val)) {
+ if (val > IQS626_CHx_ATI_TARGET_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel ATI target: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *ati_target &= ~IQS626_CHx_ATI_TARGET_MASK;
+ *ati_target |= (val / 32);
+ }
+
+ if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
+ !fwnode_property_read_u32(ch_node, "azoteq,ati-base", &val)) {
+ switch (val) {
+ case 75:
+ val = IQS626_CHx_ATI_BASE_75;
+ break;
+
+ case 100:
+ val = IQS626_CHx_ATI_BASE_100;
+ break;
+
+ case 150:
+ val = IQS626_CHx_ATI_BASE_150;
+ break;
+
+ case 200:
+ val = IQS626_CHx_ATI_BASE_200;
+ break;
+
+ default:
+ dev_err(&client->dev,
+ "Invalid %s channel ATI base: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *ati_target &= ~IQS626_CHx_ATI_BASE_MASK;
+ *ati_target |= val;
+ }
+
+ return 0;
+}
+
+static int iqs626_parse_pins(struct iqs626_private *iqs626,
+ struct fwnode_handle *ch_node,
+ const char *propname, u8 *enable)
+{
+ struct i2c_client *client = iqs626->client;
+ unsigned int val[IQS626_NUM_CRx_TX];
+ int error, count, i;
+
+ if (!fwnode_property_present(ch_node, propname))
+ return 0;
+
+ count = fwnode_property_count_u32(ch_node, propname);
+ if (count > IQS626_NUM_CRx_TX) {
+ dev_err(&client->dev,
+ "Too many %s channel CRX/TX pins present\n",
+ fwnode_get_name(ch_node));
+ return -EINVAL;
+ } else if (count < 0) {
+ dev_err(&client->dev,
+ "Failed to count %s channel CRX/TX pins: %d\n",
+ fwnode_get_name(ch_node), count);
+ return count;
+ }
+
+ error = fwnode_property_read_u32_array(ch_node, propname, val, count);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read %s channel CRX/TX pins: %d\n",
+ fwnode_get_name(ch_node), error);
+ return error;
+ }
+
+ *enable = 0;
+
+ for (i = 0; i < count; i++) {
+ if (val[i] >= IQS626_NUM_CRx_TX) {
+ dev_err(&client->dev,
+ "Invalid %s channel CRX/TX pin: %u\n",
+ fwnode_get_name(ch_node), val[i]);
+ return -EINVAL;
+ }
+
+ *enable |= BIT(val[i]);
+ }
+
+ return 0;
+}
+
+static int iqs626_parse_trackpad(struct iqs626_private *iqs626,
+ struct fwnode_handle *ch_node,
+ enum iqs626_ch_id ch_id)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ u8 *hyst = &sys_reg->tp_grp_reg.hyst;
+ int error, count, i;
+ unsigned int val;
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,lta-update", &val)) {
+ if (val > IQS626_MISC_A_TPx_LTA_UPDATE_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel update rate: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ sys_reg->misc_a &= ~IQS626_MISC_A_TPx_LTA_UPDATE_MASK;
+ sys_reg->misc_a |= (val << IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-trackpad",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_FILT_STR_TPx;
+ sys_reg->misc_b |= val;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *hyst &= ~IQS626_FILT_STR_NP_TPx_MASK;
+ *hyst |= (val << IQS626_FILT_STR_NP_TPx_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *hyst &= ~IQS626_FILT_STR_LP_TPx_MASK;
+ *hyst |= (val << IQS626_FILT_STR_LP_TPx_SHIFT);
+ }
+
+ for (i = 0; i < iqs626_channels[ch_id].num_ch; i++) {
+ u8 *ati_base = &sys_reg->tp_grp_reg.ch_reg_tp[i].ati_base;
+ u8 *thresh = &sys_reg->tp_grp_reg.ch_reg_tp[i].thresh;
+ char tc_name[10];
+
+ scnprintf(tc_name, sizeof(tc_name), "channel-%d", i);
+
+ struct fwnode_handle *tc_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(ch_node, tc_name);
+ if (!tc_node)
+ continue;
+
+ if (!fwnode_property_read_u32(tc_node, "azoteq,ati-base",
+ &val)) {
+ if (val < IQS626_TPx_ATI_BASE_MIN ||
+ val > IQS626_TPx_ATI_BASE_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s %s ATI base: %u\n",
+ fwnode_get_name(ch_node), tc_name, val);
+ return -EINVAL;
+ }
+
+ *ati_base = val - IQS626_TPx_ATI_BASE_MIN;
+ }
+
+ if (!fwnode_property_read_u32(tc_node, "azoteq,thresh",
+ &val)) {
+ if (val > IQS626_CHx_THRESH_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s %s threshold: %u\n",
+ fwnode_get_name(ch_node), tc_name, val);
+ return -EINVAL;
+ }
+
+ *thresh = val;
+ }
+ }
+
+ if (!fwnode_property_present(ch_node, "linux,keycodes"))
+ return 0;
+
+ count = fwnode_property_count_u32(ch_node, "linux,keycodes");
+ if (count > IQS626_NUM_GESTURES) {
+ dev_err(&client->dev, "Too many keycodes present\n");
+ return -EINVAL;
+ } else if (count < 0) {
+ dev_err(&client->dev, "Failed to count keycodes: %d\n", count);
+ return count;
+ }
+
+ error = fwnode_property_read_u32_array(ch_node, "linux,keycodes",
+ iqs626->tp_code, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read keycodes: %d\n", error);
+ return error;
+ }
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_TPx_SWIPE;
+ if (fwnode_property_present(ch_node, "azoteq,gesture-swipe"))
+ sys_reg->misc_b |= IQS626_MISC_B_TPx_SWIPE;
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-tap-ms",
+ &val)) {
+ if (val > IQS626_TIMEOUT_TAP_MS_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel timeout: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_tap = val / 16;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-swipe-ms",
+ &val)) {
+ if (val > IQS626_TIMEOUT_SWIPE_MS_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel timeout: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_swipe = val / 16;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,thresh-swipe",
+ &val)) {
+ if (val > IQS626_THRESH_SWIPE_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel threshold: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ sys_reg->thresh_swipe = val;
+ }
+
+ sys_reg->event_mask &= ~IQS626_EVENT_MASK_GESTURE;
+
+ return 0;
+}
+
+static noinline_for_stack int
+iqs626_parse_channel(struct iqs626_private *iqs626,
+ struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ u8 *engine, *filter, *rx_enable, *tx_enable;
+ u8 *assoc_select, *assoc_weight;
+ unsigned int val;
+ int error, i;
+
+ switch (ch_id) {
+ case IQS626_CH_ULP_0:
+ engine = sys_reg->ch_reg_ulp.engine;
+ break;
+
+ case IQS626_CH_TP_2:
+ case IQS626_CH_TP_3:
+ engine = sys_reg->tp_grp_reg.engine;
+ break;
+
+ case IQS626_CH_GEN_0:
+ case IQS626_CH_GEN_1:
+ case IQS626_CH_GEN_2:
+ i = ch_id - IQS626_CH_GEN_0;
+ engine = sys_reg->ch_reg_gen[i].engine;
+ break;
+
+ case IQS626_CH_HALL:
+ engine = &sys_reg->ch_reg_hall.engine;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ error = iqs626_parse_ati_target(iqs626, ch_node, ch_id);
+ if (error)
+ return error;
+
+ error = iqs626_parse_events(iqs626, ch_node, ch_id);
+ if (error)
+ return error;
+
+ if (!fwnode_property_present(ch_node, "azoteq,ati-exclude"))
+ sys_reg->redo_ati |= iqs626_channels[ch_id].active;
+
+ if (!fwnode_property_present(ch_node, "azoteq,reseed-disable"))
+ sys_reg->reseed |= iqs626_channels[ch_id].active;
+
+ *engine |= IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
+ if (fwnode_property_present(ch_node, "azoteq,meas-cap-decrease"))
+ *engine &= ~IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
+
+ *engine |= IQS626_CHx_ENG_0_RX_TERM_VSS;
+ if (!fwnode_property_read_u32(ch_node, "azoteq,rx-inactive", &val)) {
+ switch (val) {
+ case IQS626_RX_INACTIVE_VSS:
+ break;
+
+ case IQS626_RX_INACTIVE_FLOAT:
+ *engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
+ if (ch_id == IQS626_CH_GEN_0 ||
+ ch_id == IQS626_CH_GEN_1 ||
+ ch_id == IQS626_CH_GEN_2)
+ *(engine + 4) &= ~IQS626_CHx_ENG_4_RX_TERM_VREG;
+ break;
+
+ case IQS626_RX_INACTIVE_VREG:
+ if (ch_id == IQS626_CH_GEN_0 ||
+ ch_id == IQS626_CH_GEN_1 ||
+ ch_id == IQS626_CH_GEN_2) {
+ *engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
+ *(engine + 4) |= IQS626_CHx_ENG_4_RX_TERM_VREG;
+ break;
+ }
+ fallthrough;
+
+ default:
+ dev_err(&client->dev,
+ "Invalid %s channel CRX pin termination: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+ }
+
+ *engine &= ~IQS626_CHx_ENG_0_LINEARIZE;
+ if (fwnode_property_present(ch_node, "azoteq,linearize"))
+ *engine |= IQS626_CHx_ENG_0_LINEARIZE;
+
+ *engine &= ~IQS626_CHx_ENG_0_DUAL_DIR;
+ if (fwnode_property_present(ch_node, "azoteq,dual-direction"))
+ *engine |= IQS626_CHx_ENG_0_DUAL_DIR;
+
+ *engine &= ~IQS626_CHx_ENG_0_FILT_DISABLE;
+ if (fwnode_property_present(ch_node, "azoteq,filt-disable"))
+ *engine |= IQS626_CHx_ENG_0_FILT_DISABLE;
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,ati-mode", &val)) {
+ if (val > IQS626_CHx_ENG_0_ATI_MODE_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel ATI mode: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *engine &= ~IQS626_CHx_ENG_0_ATI_MODE_MASK;
+ *engine |= val;
+ }
+
+ if (ch_id == IQS626_CH_HALL)
+ return 0;
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_ENABLE;
+ if (!fwnode_property_read_u32(ch_node, "azoteq,cct-increase",
+ &val) && val) {
+ unsigned int orig_val = val--;
+
+ /*
+ * In the case of the generic channels, the charge cycle time
+ * field doubles in size and straddles two separate registers.
+ */
+ if (ch_id == IQS626_CH_GEN_0 ||
+ ch_id == IQS626_CH_GEN_1 ||
+ ch_id == IQS626_CH_GEN_2) {
+ *(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_1;
+ if (val & BIT(1))
+ *(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_1;
+
+ *(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_0;
+ if (val & BIT(0))
+ *(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_0;
+
+ val >>= 2;
+ }
+
+ if (val & ~GENMASK(1, 0)) {
+ dev_err(&client->dev,
+ "Invalid %s channel charge cycle time: %u\n",
+ fwnode_get_name(ch_node), orig_val);
+ return -EINVAL;
+ }
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_1;
+ if (val & BIT(1))
+ *(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_1;
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_0;
+ if (val & BIT(0))
+ *(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_0;
+
+ *(engine + 1) |= IQS626_CHx_ENG_1_CCT_ENABLE;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,proj-bias", &val)) {
+ if (val > IQS626_CHx_ENG_1_PROJ_BIAS_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel bias current: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_PROJ_BIAS_MASK;
+ *(engine + 1) |= (val << IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,sense-freq", &val)) {
+ if (val > IQS626_CHx_ENG_1_SENSE_FREQ_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel sensing frequency: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_SENSE_FREQ_MASK;
+ *(engine + 1) |= (val << IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT);
+ }
+
+ *(engine + 1) &= ~IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
+ if (fwnode_property_present(ch_node, "azoteq,ati-band-tighten"))
+ *(engine + 1) |= IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
+
+ if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3)
+ return iqs626_parse_trackpad(iqs626, ch_node, ch_id);
+
+ if (ch_id == IQS626_CH_ULP_0) {
+ sys_reg->ch_reg_ulp.hyst &= ~IQS626_ULP_PROJ_ENABLE;
+ if (fwnode_property_present(ch_node, "azoteq,proj-enable"))
+ sys_reg->ch_reg_ulp.hyst |= IQS626_ULP_PROJ_ENABLE;
+
+ filter = &sys_reg->ch_reg_ulp.filter;
+
+ rx_enable = &sys_reg->ch_reg_ulp.rx_enable;
+ tx_enable = &sys_reg->ch_reg_ulp.tx_enable;
+ } else {
+ i = ch_id - IQS626_CH_GEN_0;
+ filter = &sys_reg->ch_reg_gen[i].filter;
+
+ rx_enable = &sys_reg->ch_reg_gen[i].rx_enable;
+ tx_enable = &sys_reg->ch_reg_gen[i].tx_enable;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *filter &= ~IQS626_FILT_STR_NP_CNT_MASK;
+ *filter |= (val << IQS626_FILT_STR_NP_CNT_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *filter &= ~IQS626_FILT_STR_LP_CNT_MASK;
+ *filter |= (val << IQS626_FILT_STR_LP_CNT_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-lta",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *filter &= ~IQS626_FILT_STR_NP_LTA_MASK;
+ *filter |= (val << IQS626_FILT_STR_NP_LTA_SHIFT);
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-lta",
+ &val)) {
+ if (val > IQS626_FILT_STR_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel filter strength: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *filter &= ~IQS626_FILT_STR_LP_LTA_MASK;
+ *filter |= val;
+ }
+
+ error = iqs626_parse_pins(iqs626, ch_node, "azoteq,rx-enable",
+ rx_enable);
+ if (error)
+ return error;
+
+ error = iqs626_parse_pins(iqs626, ch_node, "azoteq,tx-enable",
+ tx_enable);
+ if (error)
+ return error;
+
+ if (ch_id == IQS626_CH_ULP_0)
+ return 0;
+
+ *(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
+ if (!fwnode_property_read_u32(ch_node, "azoteq,local-cap-size",
+ &val) && val) {
+ unsigned int orig_val = val--;
+
+ if (val > IQS626_CHx_ENG_2_LOCAL_CAP_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel local cap. size: %u\n",
+ fwnode_get_name(ch_node), orig_val);
+ return -EINVAL;
+ }
+
+ *(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_MASK;
+ *(engine + 2) |= (val << IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT);
+
+ *(engine + 2) |= IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,sense-mode", &val)) {
+ if (val > IQS626_CHx_ENG_2_SENSE_MODE_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel sensing mode: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *(engine + 2) &= ~IQS626_CHx_ENG_2_SENSE_MODE_MASK;
+ *(engine + 2) |= val;
+ }
+
+ if (!fwnode_property_read_u32(ch_node, "azoteq,tx-freq", &val)) {
+ if (val > IQS626_CHx_ENG_3_TX_FREQ_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel excitation frequency: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *(engine + 3) &= ~IQS626_CHx_ENG_3_TX_FREQ_MASK;
+ *(engine + 3) |= (val << IQS626_CHx_ENG_3_TX_FREQ_SHIFT);
+ }
+
+ *(engine + 3) &= ~IQS626_CHx_ENG_3_INV_LOGIC;
+ if (fwnode_property_present(ch_node, "azoteq,invert-enable"))
+ *(engine + 3) |= IQS626_CHx_ENG_3_INV_LOGIC;
+
+ *(engine + 4) &= ~IQS626_CHx_ENG_4_COMP_DISABLE;
+ if (fwnode_property_present(ch_node, "azoteq,comp-disable"))
+ *(engine + 4) |= IQS626_CHx_ENG_4_COMP_DISABLE;
+
+ *(engine + 4) &= ~IQS626_CHx_ENG_4_STATIC_ENABLE;
+ if (fwnode_property_present(ch_node, "azoteq,static-enable"))
+ *(engine + 4) |= IQS626_CHx_ENG_4_STATIC_ENABLE;
+
+ i = ch_id - IQS626_CH_GEN_0;
+ assoc_select = &sys_reg->ch_reg_gen[i].assoc_select;
+ assoc_weight = &sys_reg->ch_reg_gen[i].assoc_weight;
+
+ *assoc_select = 0;
+ if (!fwnode_property_present(ch_node, "azoteq,assoc-select"))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+ if (fwnode_property_match_string(ch_node, "azoteq,assoc-select",
+ iqs626_channels[i].name) < 0)
+ continue;
+
+ *assoc_select |= iqs626_channels[i].active;
+ }
+
+ if (fwnode_property_read_u32(ch_node, "azoteq,assoc-weight", &val))
+ return 0;
+
+ if (val > IQS626_GEN_WEIGHT_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s channel associated weight: %u\n",
+ fwnode_get_name(ch_node), val);
+ return -EINVAL;
+ }
+
+ *assoc_weight = val;
+
+ return 0;
+}
+
+static int iqs626_parse_prop(struct iqs626_private *iqs626)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ unsigned int val;
+ int error, i;
+ u16 general;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,suspend-mode",
+ &val)) {
+ if (val > IQS626_SYS_SETTINGS_PWR_MODE_MAX) {
+ dev_err(&client->dev, "Invalid suspend mode: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ iqs626->suspend_mode = val;
+ }
+
+ error = regmap_raw_read(iqs626->regmap, IQS626_SYS_SETTINGS, sys_reg,
+ sizeof(*sys_reg));
+ if (error)
+ return error;
+
+ general = be16_to_cpu(sys_reg->general);
+ general &= IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
+
+ if (device_property_present(&client->dev, "azoteq,clk-div"))
+ general |= IQS626_SYS_SETTINGS_CLK_DIV;
+
+ if (device_property_present(&client->dev, "azoteq,ulp-enable"))
+ general |= IQS626_SYS_SETTINGS_ULP_AUTO;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,ulp-update",
+ &val)) {
+ if (val > IQS626_SYS_SETTINGS_ULP_UPDATE_MAX) {
+ dev_err(&client->dev, "Invalid update rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ general &= ~IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
+ general |= (val << IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT);
+ }
+
+ sys_reg->misc_a &= ~IQS626_MISC_A_ATI_BAND_DISABLE;
+ if (device_property_present(&client->dev, "azoteq,ati-band-disable"))
+ sys_reg->misc_a |= IQS626_MISC_A_ATI_BAND_DISABLE;
+
+ sys_reg->misc_a &= ~IQS626_MISC_A_ATI_LP_ONLY;
+ if (device_property_present(&client->dev, "azoteq,ati-lp-only"))
+ sys_reg->misc_a |= IQS626_MISC_A_ATI_LP_ONLY;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,gpio3-select",
+ &val)) {
+ if (val > IQS626_MISC_A_GPIO3_SELECT_MAX) {
+ dev_err(&client->dev, "Invalid GPIO3 selection: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->misc_a &= ~IQS626_MISC_A_GPIO3_SELECT_MASK;
+ sys_reg->misc_a |= val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,reseed-select",
+ &val)) {
+ if (val > IQS626_MISC_B_RESEED_UI_SEL_MAX) {
+ dev_err(&client->dev, "Invalid reseed selection: %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_UI_SEL_MASK;
+ sys_reg->misc_b |= (val << IQS626_MISC_B_RESEED_UI_SEL_SHIFT);
+ }
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_THRESH_EXTEND;
+ if (device_property_present(&client->dev, "azoteq,thresh-extend"))
+ sys_reg->misc_b |= IQS626_MISC_B_THRESH_EXTEND;
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_TRACKING_UI_ENABLE;
+ if (device_property_present(&client->dev, "azoteq,tracking-enable"))
+ sys_reg->misc_b |= IQS626_MISC_B_TRACKING_UI_ENABLE;
+
+ sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_OFFSET;
+ if (device_property_present(&client->dev, "azoteq,reseed-offset"))
+ sys_reg->misc_b |= IQS626_MISC_B_RESEED_OFFSET;
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-np-ms",
+ &val)) {
+ if (val > IQS626_RATE_NP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_np = val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-lp-ms",
+ &val)) {
+ if (val > IQS626_RATE_LP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_lp = val;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,rate-ulp-ms",
+ &val)) {
+ if (val > IQS626_RATE_ULP_MS_MAX) {
+ dev_err(&client->dev, "Invalid report rate: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->rate_ulp = val / 16;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,timeout-pwr-ms",
+ &val)) {
+ if (val > IQS626_TIMEOUT_PWR_MS_MAX) {
+ dev_err(&client->dev, "Invalid timeout: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_pwr = val / 512;
+ }
+
+ if (!device_property_read_u32(&client->dev, "azoteq,timeout-lta-ms",
+ &val)) {
+ if (val > IQS626_TIMEOUT_LTA_MS_MAX) {
+ dev_err(&client->dev, "Invalid timeout: %u\n", val);
+ return -EINVAL;
+ }
+
+ sys_reg->timeout_lta = val / 512;
+ }
+
+ sys_reg->event_mask = ~((u8)IQS626_EVENT_MASK_SYS);
+ sys_reg->redo_ati = 0;
+
+ sys_reg->reseed = 0;
+ sys_reg->active = 0;
+
+ for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+ struct fwnode_handle *ch_node __free(fwnode_handle) =
+ device_get_named_child_node(&client->dev,
+ iqs626_channels[i].name);
+ if (!ch_node)
+ continue;
+
+ error = iqs626_parse_channel(iqs626, ch_node, i);
+ if (error)
+ return error;
+
+ sys_reg->active |= iqs626_channels[i].active;
+ }
+
+ general |= IQS626_SYS_SETTINGS_EVENT_MODE;
+
+ /*
+ * Enable streaming during normal-power mode if the trackpad is used to
+ * report raw coordinates instead of gestures. In that case, the device
+ * returns to event mode during low-power mode.
+ */
+ if (sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active &&
+ sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE)
+ general |= IQS626_SYS_SETTINGS_EVENT_MODE_LP;
+
+ general |= IQS626_SYS_SETTINGS_REDO_ATI;
+ general |= IQS626_SYS_SETTINGS_ACK_RESET;
+
+ sys_reg->general = cpu_to_be16(general);
+
+ error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
+ &iqs626->sys_reg, sizeof(iqs626->sys_reg));
+ if (error)
+ return error;
+
+ iqs626_irq_wait();
+
+ return 0;
+}
+
+static int iqs626_input_init(struct iqs626_private *iqs626)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ int error, i, j;
+
+ iqs626->keypad = devm_input_allocate_device(&client->dev);
+ if (!iqs626->keypad)
+ return -ENOMEM;
+
+ iqs626->keypad->keycodemax = ARRAY_SIZE(iqs626->kp_code);
+ iqs626->keypad->keycode = iqs626->kp_code;
+ iqs626->keypad->keycodesize = sizeof(**iqs626->kp_code);
+
+ iqs626->keypad->name = "iqs626a_keypad";
+ iqs626->keypad->id.bustype = BUS_I2C;
+
+ for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+ if (!(sys_reg->active & iqs626_channels[i].active))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
+ if (!iqs626->kp_type[i][j])
+ continue;
+
+ input_set_capability(iqs626->keypad,
+ iqs626->kp_type[i][j],
+ iqs626->kp_code[i][j]);
+ }
+ }
+
+ if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
+ return 0;
+
+ iqs626->trackpad = devm_input_allocate_device(&client->dev);
+ if (!iqs626->trackpad)
+ return -ENOMEM;
+
+ iqs626->trackpad->keycodemax = ARRAY_SIZE(iqs626->tp_code);
+ iqs626->trackpad->keycode = iqs626->tp_code;
+ iqs626->trackpad->keycodesize = sizeof(*iqs626->tp_code);
+
+ iqs626->trackpad->name = "iqs626a_trackpad";
+ iqs626->trackpad->id.bustype = BUS_I2C;
+
+ /*
+ * Present the trackpad as a traditional pointing device if no gestures
+ * have been mapped to a keycode.
+ */
+ if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
+ u8 tp_mask = iqs626_channels[IQS626_CH_TP_3].active;
+
+ input_set_capability(iqs626->trackpad, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(iqs626->trackpad, ABS_Y, 0, 255, 0, 0);
+
+ if ((sys_reg->active & tp_mask) == tp_mask)
+ input_set_abs_params(iqs626->trackpad,
+ ABS_X, 0, 255, 0, 0);
+ else
+ input_set_abs_params(iqs626->trackpad,
+ ABS_X, 0, 128, 0, 0);
+
+ touchscreen_parse_properties(iqs626->trackpad, false,
+ &iqs626->prop);
+ } else {
+ for (i = 0; i < IQS626_NUM_GESTURES; i++)
+ if (iqs626->tp_code[i] != KEY_RESERVED)
+ input_set_capability(iqs626->trackpad, EV_KEY,
+ iqs626->tp_code[i]);
+ }
+
+ error = input_register_device(iqs626->trackpad);
+ if (error)
+ dev_err(&client->dev, "Failed to register trackpad: %d\n",
+ error);
+
+ return error;
+}
+
+static int iqs626_report(struct iqs626_private *iqs626)
+{
+ struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+ struct i2c_client *client = iqs626->client;
+ struct iqs626_flags flags;
+ __le16 hall_output;
+ int error, i, j;
+ u8 state;
+ u8 *dir_mask = &flags.states[IQS626_ST_OFFS_DIR];
+
+ error = regmap_raw_read(iqs626->regmap, IQS626_SYS_FLAGS, &flags,
+ sizeof(flags));
+ if (error) {
+ dev_err(&client->dev, "Failed to read device status: %d\n",
+ error);
+ return error;
+ }
+
+ /*
+ * The device resets itself if its own watchdog bites, which can happen
+ * in the event of an I2C communication error. In this case, the device
+ * asserts a SHOW_RESET interrupt and all registers must be restored.
+ */
+ if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_SHOW_RESET) {
+ dev_err(&client->dev, "Unexpected device reset\n");
+
+ error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
+ sys_reg, sizeof(*sys_reg));
+ if (error)
+ dev_err(&client->dev,
+ "Failed to re-initialize device: %d\n", error);
+
+ return error;
+ }
+
+ if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_IN_ATI)
+ return 0;
+
+ /*
+ * Unlike the ULP or generic channels, the Hall channel does not have a
+ * direction flag. Instead, the direction (i.e. magnet polarity) can be
+ * derived based on the sign of the 2's complement differential output.
+ */
+ if (sys_reg->active & iqs626_channels[IQS626_CH_HALL].active) {
+ error = regmap_raw_read(iqs626->regmap, IQS626_HALL_OUTPUT,
+ &hall_output, sizeof(hall_output));
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read Hall output: %d\n", error);
+ return error;
+ }
+
+ *dir_mask &= ~iqs626_channels[IQS626_CH_HALL].active;
+ if (le16_to_cpu(hall_output) < 0x8000)
+ *dir_mask |= iqs626_channels[IQS626_CH_HALL].active;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+ if (!(sys_reg->active & iqs626_channels[i].active))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
+ if (!iqs626->kp_type[i][j])
+ continue;
+
+ state = flags.states[iqs626_events[j].st_offs];
+ state &= iqs626_events[j].dir_up ? *dir_mask
+ : ~(*dir_mask);
+ state &= iqs626_channels[i].active;
+
+ input_event(iqs626->keypad, iqs626->kp_type[i][j],
+ iqs626->kp_code[i][j], !!state);
+ }
+ }
+
+ input_sync(iqs626->keypad);
+
+ /*
+ * The following completion signals that ATI has finished, any initial
+ * switch states have been reported and the keypad can be registered.
+ */
+ complete_all(&iqs626->ati_done);
+
+ if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
+ return 0;
+
+ if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
+ state = flags.states[IQS626_ST_OFFS_TOUCH];
+ state &= iqs626_channels[IQS626_CH_TP_2].active;
+
+ input_report_key(iqs626->trackpad, BTN_TOUCH, state);
+
+ if (state)
+ touchscreen_report_pos(iqs626->trackpad, &iqs626->prop,
+ flags.trackpad_x,
+ flags.trackpad_y, false);
+ } else {
+ for (i = 0; i < IQS626_NUM_GESTURES; i++)
+ input_report_key(iqs626->trackpad, iqs626->tp_code[i],
+ flags.gesture & BIT(i));
+
+ if (flags.gesture & GENMASK(IQS626_GESTURE_TAP, 0)) {
+ input_sync(iqs626->trackpad);
+
+ /*
+ * Momentary gestures are followed by a complementary
+ * release cycle so as to emulate a full keystroke.
+ */
+ for (i = 0; i < IQS626_GESTURE_HOLD; i++)
+ input_report_key(iqs626->trackpad,
+ iqs626->tp_code[i], 0);
+ }
+ }
+
+ input_sync(iqs626->trackpad);
+
+ return 0;
+}
+
+static irqreturn_t iqs626_irq(int irq, void *context)
+{
+ struct iqs626_private *iqs626 = context;
+
+ if (iqs626_report(iqs626))
+ return IRQ_NONE;
+
+ /*
+ * The device does not deassert its interrupt (RDY) pin until shortly
+ * after receiving an I2C stop condition; the following delay ensures
+ * the interrupt handler does not return before this time.
+ */
+ iqs626_irq_wait();
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config iqs626_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = IQS626_MAX_REG,
+};
+
+static int iqs626_probe(struct i2c_client *client)
+{
+ struct iqs626_ver_info ver_info;
+ struct iqs626_private *iqs626;
+ int error;
+
+ iqs626 = devm_kzalloc(&client->dev, sizeof(*iqs626), GFP_KERNEL);
+ if (!iqs626)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, iqs626);
+ iqs626->client = client;
+
+ iqs626->regmap = devm_regmap_init_i2c(client, &iqs626_regmap_config);
+ if (IS_ERR(iqs626->regmap)) {
+ error = PTR_ERR(iqs626->regmap);
+ dev_err(&client->dev, "Failed to initialize register map: %d\n",
+ error);
+ return error;
+ }
+
+ init_completion(&iqs626->ati_done);
+
+ error = regmap_raw_read(iqs626->regmap, IQS626_VER_INFO, &ver_info,
+ sizeof(ver_info));
+ if (error)
+ return error;
+
+ if (ver_info.prod_num != IQS626_VER_INFO_PROD_NUM) {
+ dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
+ ver_info.prod_num);
+ return -EINVAL;
+ }
+
+ error = iqs626_parse_prop(iqs626);
+ if (error)
+ return error;
+
+ error = iqs626_input_init(iqs626);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, iqs626_irq, IRQF_ONESHOT,
+ client->name, iqs626);
+ if (error) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+ return error;
+ }
+
+ if (!wait_for_completion_timeout(&iqs626->ati_done,
+ msecs_to_jiffies(2000))) {
+ dev_err(&client->dev, "Failed to complete ATI\n");
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * The keypad may include one or more switches and is not registered
+ * until ATI is complete and the initial switch states are read.
+ */
+ error = input_register_device(iqs626->keypad);
+ if (error)
+ dev_err(&client->dev, "Failed to register keypad: %d\n", error);
+
+ return error;
+}
+
+static int iqs626_suspend(struct device *dev)
+{
+ struct iqs626_private *iqs626 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs626->client;
+ unsigned int val;
+ int error;
+
+ if (!iqs626->suspend_mode)
+ return 0;
+
+ disable_irq(client->irq);
+
+ /*
+ * Automatic power mode switching must be disabled before the device is
+ * forced into any particular power mode. In this case, the device will
+ * transition into normal-power mode.
+ */
+ error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+ IQS626_SYS_SETTINGS_DIS_AUTO, ~0);
+ if (error)
+ goto err_irq;
+
+ /*
+ * The following check ensures the device has completed its transition
+ * into normal-power mode before a manual mode switch is performed.
+ */
+ error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+ !(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
+ IQS626_PWR_MODE_POLL_SLEEP_US,
+ IQS626_PWR_MODE_POLL_TIMEOUT_US);
+ if (error)
+ goto err_irq;
+
+ error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+ IQS626_SYS_SETTINGS_PWR_MODE_MASK,
+ iqs626->suspend_mode <<
+ IQS626_SYS_SETTINGS_PWR_MODE_SHIFT);
+ if (error)
+ goto err_irq;
+
+ /*
+ * This last check ensures the device has completed its transition into
+ * the desired power mode to prevent any spurious interrupts from being
+ * triggered after iqs626_suspend has already returned.
+ */
+ error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+ (val & IQS626_SYS_FLAGS_PWR_MODE_MASK)
+ == (iqs626->suspend_mode <<
+ IQS626_SYS_FLAGS_PWR_MODE_SHIFT),
+ IQS626_PWR_MODE_POLL_SLEEP_US,
+ IQS626_PWR_MODE_POLL_TIMEOUT_US);
+
+err_irq:
+ iqs626_irq_wait();
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static int iqs626_resume(struct device *dev)
+{
+ struct iqs626_private *iqs626 = dev_get_drvdata(dev);
+ struct i2c_client *client = iqs626->client;
+ unsigned int val;
+ int error;
+
+ if (!iqs626->suspend_mode)
+ return 0;
+
+ disable_irq(client->irq);
+
+ error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+ IQS626_SYS_SETTINGS_PWR_MODE_MASK, 0);
+ if (error)
+ goto err_irq;
+
+ /*
+ * This check ensures the device has returned to normal-power mode
+ * before automatic power mode switching is re-enabled.
+ */
+ error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+ !(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
+ IQS626_PWR_MODE_POLL_SLEEP_US,
+ IQS626_PWR_MODE_POLL_TIMEOUT_US);
+ if (error)
+ goto err_irq;
+
+ error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+ IQS626_SYS_SETTINGS_DIS_AUTO, 0);
+ if (error)
+ goto err_irq;
+
+ /*
+ * This step reports any events that may have been "swallowed" as a
+ * result of polling PWR_MODE (which automatically acknowledges any
+ * pending interrupts).
+ */
+ error = iqs626_report(iqs626);
+
+err_irq:
+ iqs626_irq_wait();
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(iqs626_pm, iqs626_suspend, iqs626_resume);
+
+static const struct of_device_id iqs626_of_match[] = {
+ { .compatible = "azoteq,iqs626a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, iqs626_of_match);
+
+static struct i2c_driver iqs626_i2c_driver = {
+ .driver = {
+ .name = "iqs626a",
+ .of_match_table = iqs626_of_match,
+ .pm = pm_sleep_ptr(&iqs626_pm),
+ },
+ .probe = iqs626_probe,
+};
+module_i2c_driver(iqs626_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS626A Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c
new file mode 100644
index 000000000000..ff23219a582a
--- /dev/null
+++ b/drivers/input/misc/iqs7222.c
@@ -0,0 +1,3162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Azoteq IQS7222A/B/C/D Capacitive Touch Controller
+ *
+ * Copyright (C) 2022 Jeff LaBundy <jeff@labundy.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+
+#define IQS7222_PROD_NUM 0x00
+#define IQS7222_PROD_NUM_A 840
+#define IQS7222_PROD_NUM_B 698
+#define IQS7222_PROD_NUM_C 863
+#define IQS7222_PROD_NUM_D 1046
+
+#define IQS7222_SYS_STATUS 0x10
+#define IQS7222_SYS_STATUS_RESET BIT(3)
+#define IQS7222_SYS_STATUS_ATI_ERROR BIT(1)
+#define IQS7222_SYS_STATUS_ATI_ACTIVE BIT(0)
+
+#define IQS7222_CHAN_SETUP_0_REF_MODE_MASK GENMASK(15, 14)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW BIT(15)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_REF BIT(14)
+#define IQS7222_CHAN_SETUP_0_CHAN_EN BIT(8)
+
+#define IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK GENMASK(2, 0)
+#define IQS7222_SLDR_SETUP_2_RES_MASK GENMASK(15, 8)
+#define IQS7222_SLDR_SETUP_2_RES_SHIFT 8
+#define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK GENMASK(7, 0)
+
+#define IQS7222_GPIO_SETUP_0_GPIO_EN BIT(0)
+
+#define IQS7222_SYS_SETUP 0xD0
+#define IQS7222_SYS_SETUP_INTF_MODE_MASK GENMASK(7, 6)
+#define IQS7222_SYS_SETUP_INTF_MODE_TOUCH BIT(7)
+#define IQS7222_SYS_SETUP_INTF_MODE_EVENT BIT(6)
+#define IQS7222_SYS_SETUP_PWR_MODE_MASK GENMASK(5, 4)
+#define IQS7222_SYS_SETUP_PWR_MODE_AUTO IQS7222_SYS_SETUP_PWR_MODE_MASK
+#define IQS7222_SYS_SETUP_REDO_ATI BIT(2)
+#define IQS7222_SYS_SETUP_ACK_RESET BIT(0)
+
+#define IQS7222_EVENT_MASK_ATI BIT(12)
+#define IQS7222_EVENT_MASK_SLDR BIT(10)
+#define IQS7222_EVENT_MASK_TPAD IQS7222_EVENT_MASK_SLDR
+#define IQS7222_EVENT_MASK_TOUCH BIT(1)
+#define IQS7222_EVENT_MASK_PROX BIT(0)
+
+#define IQS7222_COMMS_HOLD BIT(0)
+#define IQS7222_COMMS_ERROR 0xEEEE
+#define IQS7222_COMMS_RETRY_MS 50
+#define IQS7222_COMMS_TIMEOUT_MS 100
+#define IQS7222_RESET_TIMEOUT_MS 250
+#define IQS7222_ATI_TIMEOUT_MS 2000
+
+#define IQS7222_MAX_COLS_STAT 8
+#define IQS7222_MAX_COLS_CYCLE 3
+#define IQS7222_MAX_COLS_GLBL 3
+#define IQS7222_MAX_COLS_BTN 3
+#define IQS7222_MAX_COLS_CHAN 6
+#define IQS7222_MAX_COLS_FILT 2
+#define IQS7222_MAX_COLS_SLDR 11
+#define IQS7222_MAX_COLS_TPAD 24
+#define IQS7222_MAX_COLS_GPIO 3
+#define IQS7222_MAX_COLS_SYS 13
+
+#define IQS7222_MAX_CHAN 20
+#define IQS7222_MAX_SLDR 2
+
+#define IQS7222_NUM_RETRIES 5
+#define IQS7222_REG_OFFSET 0x100
+
+enum iqs7222_reg_key_id {
+ IQS7222_REG_KEY_NONE,
+ IQS7222_REG_KEY_PROX,
+ IQS7222_REG_KEY_TOUCH,
+ IQS7222_REG_KEY_DEBOUNCE,
+ IQS7222_REG_KEY_TAP,
+ IQS7222_REG_KEY_TAP_LEGACY,
+ IQS7222_REG_KEY_AXIAL,
+ IQS7222_REG_KEY_AXIAL_LEGACY,
+ IQS7222_REG_KEY_WHEEL,
+ IQS7222_REG_KEY_NO_WHEEL,
+ IQS7222_REG_KEY_RESERVED
+};
+
+enum iqs7222_reg_grp_id {
+ IQS7222_REG_GRP_STAT,
+ IQS7222_REG_GRP_CYCLE,
+ IQS7222_REG_GRP_GLBL,
+ IQS7222_REG_GRP_BTN,
+ IQS7222_REG_GRP_CHAN,
+ IQS7222_REG_GRP_FILT,
+ IQS7222_REG_GRP_SLDR,
+ IQS7222_REG_GRP_TPAD,
+ IQS7222_REG_GRP_GPIO,
+ IQS7222_REG_GRP_SYS,
+ IQS7222_NUM_REG_GRPS
+};
+
+static const char * const iqs7222_reg_grp_names[IQS7222_NUM_REG_GRPS] = {
+ [IQS7222_REG_GRP_CYCLE] = "cycle-%d",
+ [IQS7222_REG_GRP_CHAN] = "channel-%d",
+ [IQS7222_REG_GRP_SLDR] = "slider-%d",
+ [IQS7222_REG_GRP_TPAD] = "trackpad",
+ [IQS7222_REG_GRP_GPIO] = "gpio-%d",
+};
+
+static const unsigned int iqs7222_max_cols[IQS7222_NUM_REG_GRPS] = {
+ [IQS7222_REG_GRP_STAT] = IQS7222_MAX_COLS_STAT,
+ [IQS7222_REG_GRP_CYCLE] = IQS7222_MAX_COLS_CYCLE,
+ [IQS7222_REG_GRP_GLBL] = IQS7222_MAX_COLS_GLBL,
+ [IQS7222_REG_GRP_BTN] = IQS7222_MAX_COLS_BTN,
+ [IQS7222_REG_GRP_CHAN] = IQS7222_MAX_COLS_CHAN,
+ [IQS7222_REG_GRP_FILT] = IQS7222_MAX_COLS_FILT,
+ [IQS7222_REG_GRP_SLDR] = IQS7222_MAX_COLS_SLDR,
+ [IQS7222_REG_GRP_TPAD] = IQS7222_MAX_COLS_TPAD,
+ [IQS7222_REG_GRP_GPIO] = IQS7222_MAX_COLS_GPIO,
+ [IQS7222_REG_GRP_SYS] = IQS7222_MAX_COLS_SYS,
+};
+
+static const unsigned int iqs7222_gpio_links[] = { 2, 5, 6, };
+
+struct iqs7222_event_desc {
+ const char *name;
+ u16 link;
+ u16 mask;
+ u16 val;
+ u16 strict;
+ u16 enable;
+ enum iqs7222_reg_key_id reg_key;
+};
+
+static const struct iqs7222_event_desc iqs7222_kp_events[] = {
+ {
+ .name = "event-prox",
+ .enable = IQS7222_EVENT_MASK_PROX,
+ .reg_key = IQS7222_REG_KEY_PROX,
+ },
+ {
+ .name = "event-touch",
+ .enable = IQS7222_EVENT_MASK_TOUCH,
+ .reg_key = IQS7222_REG_KEY_TOUCH,
+ },
+};
+
+static const struct iqs7222_event_desc iqs7222_sl_events[] = {
+ { .name = "event-press", },
+ {
+ .name = "event-tap",
+ .mask = BIT(0),
+ .val = BIT(0),
+ .enable = BIT(0),
+ .reg_key = IQS7222_REG_KEY_TAP,
+ },
+ {
+ .name = "event-swipe-pos",
+ .mask = BIT(5) | BIT(1),
+ .val = BIT(1),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-swipe-neg",
+ .mask = BIT(5) | BIT(1),
+ .val = BIT(5) | BIT(1),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-pos",
+ .mask = BIT(5) | BIT(2),
+ .val = BIT(2),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-neg",
+ .mask = BIT(5) | BIT(2),
+ .val = BIT(5) | BIT(2),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+};
+
+static const struct iqs7222_event_desc iqs7222_tp_events[] = {
+ {
+ .name = "event-press",
+ .link = BIT(7),
+ },
+ {
+ .name = "event-tap",
+ .link = BIT(0),
+ .mask = BIT(0),
+ .val = BIT(0),
+ .enable = BIT(0),
+ .reg_key = IQS7222_REG_KEY_TAP,
+ },
+ {
+ .name = "event-swipe-x-pos",
+ .link = BIT(2),
+ .mask = BIT(2) | BIT(1),
+ .val = BIT(2),
+ .strict = BIT(4),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-swipe-y-pos",
+ .link = BIT(3),
+ .mask = BIT(3) | BIT(1),
+ .val = BIT(3),
+ .strict = BIT(3),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-swipe-x-neg",
+ .link = BIT(4),
+ .mask = BIT(4) | BIT(1),
+ .val = BIT(4),
+ .strict = BIT(4),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-swipe-y-neg",
+ .link = BIT(5),
+ .mask = BIT(5) | BIT(1),
+ .val = BIT(5),
+ .strict = BIT(3),
+ .enable = BIT(1),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-x-pos",
+ .link = BIT(2),
+ .mask = BIT(2) | BIT(1),
+ .val = BIT(2) | BIT(1),
+ .strict = BIT(4),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-y-pos",
+ .link = BIT(3),
+ .mask = BIT(3) | BIT(1),
+ .val = BIT(3) | BIT(1),
+ .strict = BIT(3),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-x-neg",
+ .link = BIT(4),
+ .mask = BIT(4) | BIT(1),
+ .val = BIT(4) | BIT(1),
+ .strict = BIT(4),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+ {
+ .name = "event-flick-y-neg",
+ .link = BIT(5),
+ .mask = BIT(5) | BIT(1),
+ .val = BIT(5) | BIT(1),
+ .strict = BIT(3),
+ .enable = BIT(2),
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ },
+};
+
+struct iqs7222_reg_grp_desc {
+ u16 base;
+ u16 val_len;
+ int num_row;
+ int num_col;
+};
+
+struct iqs7222_dev_desc {
+ u16 prod_num;
+ u16 fw_major;
+ u16 fw_minor;
+ u16 sldr_res;
+ u16 touch_link;
+ u16 wheel_enable;
+ int allow_offset;
+ int event_offset;
+ int comms_offset;
+ int ext_chan;
+ bool legacy_gesture;
+ struct iqs7222_reg_grp_desc reg_grps[IQS7222_NUM_REG_GRPS];
+};
+
+static const struct iqs7222_dev_desc iqs7222_devs[] = {
+ {
+ .prod_num = IQS7222_PROD_NUM_A,
+ .fw_major = 1,
+ .fw_minor = 13,
+ .sldr_res = U8_MAX * 16,
+ .touch_link = 1768,
+ .allow_offset = 9,
+ .event_offset = 10,
+ .comms_offset = 12,
+ .ext_chan = 10,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 8,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 7,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8700,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 12,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 12,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAC00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SLDR] = {
+ .base = 0xB000,
+ .num_row = 2,
+ .num_col = 11,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 13,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_A,
+ .fw_major = 1,
+ .fw_minor = 12,
+ .sldr_res = U8_MAX * 16,
+ .touch_link = 1768,
+ .allow_offset = 9,
+ .event_offset = 10,
+ .comms_offset = 12,
+ .ext_chan = 10,
+ .legacy_gesture = true,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 8,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 7,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8700,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 12,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 12,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAC00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SLDR] = {
+ .base = 0xB000,
+ .num_row = 2,
+ .num_col = 11,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 13,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_B,
+ .fw_major = 1,
+ .fw_minor = 43,
+ .event_offset = 10,
+ .comms_offset = 11,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 10,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8A00,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 20,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xB000,
+ .num_row = 20,
+ .num_col = 4,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xC400,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 13,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_B,
+ .fw_major = 1,
+ .fw_minor = 27,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 10,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8A00,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 20,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xB000,
+ .num_row = 20,
+ .num_col = 4,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xC400,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 10,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_C,
+ .fw_major = 2,
+ .fw_minor = 6,
+ .sldr_res = U16_MAX,
+ .touch_link = 1686,
+ .wheel_enable = BIT(3),
+ .event_offset = 9,
+ .comms_offset = 10,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 5,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8500,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 10,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 10,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAA00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SLDR] = {
+ .base = 0xB000,
+ .num_row = 2,
+ .num_col = 10,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 3,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 12,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_C,
+ .fw_major = 1,
+ .fw_minor = 13,
+ .sldr_res = U16_MAX,
+ .touch_link = 1674,
+ .wheel_enable = BIT(3),
+ .event_offset = 9,
+ .comms_offset = 10,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 5,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8500,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 10,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 10,
+ .num_col = 6,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAA00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_SLDR] = {
+ .base = 0xB000,
+ .num_row = 2,
+ .num_col = 10,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 11,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_D,
+ .fw_major = 1,
+ .fw_minor = 2,
+ .touch_link = 1770,
+ .allow_offset = 9,
+ .event_offset = 10,
+ .comms_offset = 11,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 7,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 7,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8700,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 14,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 14,
+ .num_col = 4,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAE00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_TPAD] = {
+ .base = 0xB000,
+ .num_row = 1,
+ .num_col = 24,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 3,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 12,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_D,
+ .fw_major = 1,
+ .fw_minor = 1,
+ .touch_link = 1774,
+ .allow_offset = 9,
+ .event_offset = 10,
+ .comms_offset = 11,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 7,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 7,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8700,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 14,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 14,
+ .num_col = 4,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAE00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_TPAD] = {
+ .base = 0xB000,
+ .num_row = 1,
+ .num_col = 24,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 3,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 12,
+ },
+ },
+ },
+ {
+ .prod_num = IQS7222_PROD_NUM_D,
+ .fw_major = 0,
+ .fw_minor = 37,
+ .touch_link = 1770,
+ .allow_offset = 9,
+ .event_offset = 10,
+ .comms_offset = 11,
+ .reg_grps = {
+ [IQS7222_REG_GRP_STAT] = {
+ .base = IQS7222_SYS_STATUS,
+ .num_row = 1,
+ .num_col = 7,
+ },
+ [IQS7222_REG_GRP_CYCLE] = {
+ .base = 0x8000,
+ .num_row = 7,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_GLBL] = {
+ .base = 0x8700,
+ .num_row = 1,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_BTN] = {
+ .base = 0x9000,
+ .num_row = 14,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_CHAN] = {
+ .base = 0xA000,
+ .num_row = 14,
+ .num_col = 4,
+ },
+ [IQS7222_REG_GRP_FILT] = {
+ .base = 0xAE00,
+ .val_len = 3,
+ .num_row = 1,
+ .num_col = 2,
+ },
+ [IQS7222_REG_GRP_TPAD] = {
+ .base = 0xB000,
+ .num_row = 1,
+ .num_col = 24,
+ },
+ [IQS7222_REG_GRP_GPIO] = {
+ .base = 0xC000,
+ .num_row = 3,
+ .num_col = 3,
+ },
+ [IQS7222_REG_GRP_SYS] = {
+ .base = IQS7222_SYS_SETUP,
+ .num_row = 1,
+ .num_col = 12,
+ },
+ },
+ },
+};
+
+struct iqs7222_prop_desc {
+ const char *name;
+ enum iqs7222_reg_grp_id reg_grp;
+ enum iqs7222_reg_key_id reg_key;
+ int reg_offset;
+ int reg_shift;
+ int reg_width;
+ int val_pitch;
+ int val_min;
+ int val_max;
+ bool invert;
+ const char *label;
+};
+
+static const struct iqs7222_prop_desc iqs7222_props[] = {
+ {
+ .name = "azoteq,conv-period",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 0,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "conversion period",
+ },
+ {
+ .name = "azoteq,conv-frac",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 0,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "conversion frequency fractional divider",
+ },
+ {
+ .name = "azoteq,rx-float-inactive",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 1,
+ .reg_shift = 6,
+ .reg_width = 1,
+ .invert = true,
+ },
+ {
+ .name = "azoteq,dead-time-enable",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 1,
+ .reg_shift = 5,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,tx-freq-fosc",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 1,
+ .reg_shift = 4,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,vbias-enable",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 1,
+ .reg_shift = 3,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,sense-mode",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 3,
+ .val_max = 3,
+ .label = "sensing mode",
+ },
+ {
+ .name = "azoteq,iref-enable",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 2,
+ .reg_shift = 10,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,iref-level",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 2,
+ .reg_shift = 4,
+ .reg_width = 4,
+ .label = "current reference level",
+ },
+ {
+ .name = "azoteq,iref-trim",
+ .reg_grp = IQS7222_REG_GRP_CYCLE,
+ .reg_offset = 2,
+ .reg_shift = 0,
+ .reg_width = 4,
+ .label = "current reference trim",
+ },
+ {
+ .name = "azoteq,max-counts",
+ .reg_grp = IQS7222_REG_GRP_GLBL,
+ .reg_offset = 0,
+ .reg_shift = 13,
+ .reg_width = 2,
+ .label = "maximum counts",
+ },
+ {
+ .name = "azoteq,auto-mode",
+ .reg_grp = IQS7222_REG_GRP_GLBL,
+ .reg_offset = 0,
+ .reg_shift = 2,
+ .reg_width = 2,
+ .label = "number of conversions",
+ },
+ {
+ .name = "azoteq,ati-frac-div-fine",
+ .reg_grp = IQS7222_REG_GRP_GLBL,
+ .reg_offset = 1,
+ .reg_shift = 9,
+ .reg_width = 5,
+ .label = "ATI fine fractional divider",
+ },
+ {
+ .name = "azoteq,ati-frac-div-coarse",
+ .reg_grp = IQS7222_REG_GRP_GLBL,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 5,
+ .label = "ATI coarse fractional divider",
+ },
+ {
+ .name = "azoteq,ati-comp-select",
+ .reg_grp = IQS7222_REG_GRP_GLBL,
+ .reg_offset = 2,
+ .reg_shift = 0,
+ .reg_width = 10,
+ .label = "ATI compensation selection",
+ },
+ {
+ .name = "azoteq,ati-band",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 12,
+ .reg_width = 2,
+ .label = "ATI band",
+ },
+ {
+ .name = "azoteq,global-halt",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 11,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,invert-enable",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 10,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,dual-direction",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 9,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,samp-cap-double",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 3,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,vref-half",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 2,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,proj-bias",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 0,
+ .reg_shift = 0,
+ .reg_width = 2,
+ .label = "projected bias current",
+ },
+ {
+ .name = "azoteq,ati-target",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 1,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 8,
+ .label = "ATI target",
+ },
+ {
+ .name = "azoteq,ati-base",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 1,
+ .reg_shift = 3,
+ .reg_width = 5,
+ .val_pitch = 16,
+ .label = "ATI base",
+ },
+ {
+ .name = "azoteq,ati-mode",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 3,
+ .val_max = 5,
+ .label = "ATI mode",
+ },
+ {
+ .name = "azoteq,ati-frac-div-fine",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 2,
+ .reg_shift = 9,
+ .reg_width = 5,
+ .label = "ATI fine fractional divider",
+ },
+ {
+ .name = "azoteq,ati-frac-mult-coarse",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 2,
+ .reg_shift = 5,
+ .reg_width = 4,
+ .label = "ATI coarse fractional multiplier",
+ },
+ {
+ .name = "azoteq,ati-frac-div-coarse",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 2,
+ .reg_shift = 0,
+ .reg_width = 5,
+ .label = "ATI coarse fractional divider",
+ },
+ {
+ .name = "azoteq,ati-comp-div",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 3,
+ .reg_shift = 11,
+ .reg_width = 5,
+ .label = "ATI compensation divider",
+ },
+ {
+ .name = "azoteq,ati-comp-select",
+ .reg_grp = IQS7222_REG_GRP_CHAN,
+ .reg_offset = 3,
+ .reg_shift = 0,
+ .reg_width = 10,
+ .label = "ATI compensation selection",
+ },
+ {
+ .name = "azoteq,debounce-exit",
+ .reg_grp = IQS7222_REG_GRP_BTN,
+ .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+ .reg_offset = 0,
+ .reg_shift = 12,
+ .reg_width = 4,
+ .label = "debounce exit factor",
+ },
+ {
+ .name = "azoteq,debounce-enter",
+ .reg_grp = IQS7222_REG_GRP_BTN,
+ .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+ .reg_offset = 0,
+ .reg_shift = 8,
+ .reg_width = 4,
+ .label = "debounce entrance factor",
+ },
+ {
+ .name = "azoteq,thresh",
+ .reg_grp = IQS7222_REG_GRP_BTN,
+ .reg_key = IQS7222_REG_KEY_PROX,
+ .reg_offset = 0,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_max = 127,
+ .label = "threshold",
+ },
+ {
+ .name = "azoteq,thresh",
+ .reg_grp = IQS7222_REG_GRP_BTN,
+ .reg_key = IQS7222_REG_KEY_TOUCH,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "threshold",
+ },
+ {
+ .name = "azoteq,hyst",
+ .reg_grp = IQS7222_REG_GRP_BTN,
+ .reg_key = IQS7222_REG_KEY_TOUCH,
+ .reg_offset = 1,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "hysteresis",
+ },
+ {
+ .name = "azoteq,lta-beta-lp",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 0,
+ .reg_shift = 12,
+ .reg_width = 4,
+ .label = "low-power mode long-term average beta",
+ },
+ {
+ .name = "azoteq,lta-beta-np",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 0,
+ .reg_shift = 8,
+ .reg_width = 4,
+ .label = "normal-power mode long-term average beta",
+ },
+ {
+ .name = "azoteq,counts-beta-lp",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 0,
+ .reg_shift = 4,
+ .reg_width = 4,
+ .label = "low-power mode counts beta",
+ },
+ {
+ .name = "azoteq,counts-beta-np",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 0,
+ .reg_shift = 0,
+ .reg_width = 4,
+ .label = "normal-power mode counts beta",
+ },
+ {
+ .name = "azoteq,lta-fast-beta-lp",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 1,
+ .reg_shift = 4,
+ .reg_width = 4,
+ .label = "low-power mode long-term average fast beta",
+ },
+ {
+ .name = "azoteq,lta-fast-beta-np",
+ .reg_grp = IQS7222_REG_GRP_FILT,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 4,
+ .label = "normal-power mode long-term average fast beta",
+ },
+ {
+ .name = "azoteq,lower-cal",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_offset = 0,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "lower calibration",
+ },
+ {
+ .name = "azoteq,static-beta",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+ .reg_offset = 0,
+ .reg_shift = 6,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,bottom-beta",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+ .reg_offset = 0,
+ .reg_shift = 3,
+ .reg_width = 3,
+ .label = "bottom beta",
+ },
+ {
+ .name = "azoteq,static-beta",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_WHEEL,
+ .reg_offset = 0,
+ .reg_shift = 7,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,bottom-beta",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_WHEEL,
+ .reg_offset = 0,
+ .reg_shift = 4,
+ .reg_width = 3,
+ .label = "bottom beta",
+ },
+ {
+ .name = "azoteq,bottom-speed",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_offset = 1,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "bottom speed",
+ },
+ {
+ .name = "azoteq,upper-cal",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "upper calibration",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_TAP,
+ .reg_offset = 9,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_TAP_LEGACY,
+ .reg_offset = 9,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 4,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-min-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_TAP,
+ .reg_offset = 9,
+ .reg_shift = 3,
+ .reg_width = 5,
+ .val_pitch = 16,
+ .label = "minimum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-min-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_TAP_LEGACY,
+ .reg_offset = 9,
+ .reg_shift = 3,
+ .reg_width = 5,
+ .val_pitch = 4,
+ .label = "minimum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ .reg_offset = 10,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_AXIAL_LEGACY,
+ .reg_offset = 10,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ .reg_offset = 10,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_SLDR,
+ .reg_key = IQS7222_REG_KEY_AXIAL_LEGACY,
+ .reg_offset = 10,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_pitch = 4,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,num-rows",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 0,
+ .reg_shift = 4,
+ .reg_width = 4,
+ .val_min = 1,
+ .val_max = 12,
+ .label = "number of rows",
+ },
+ {
+ .name = "azoteq,num-cols",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 0,
+ .reg_shift = 0,
+ .reg_width = 4,
+ .val_min = 1,
+ .val_max = 12,
+ .label = "number of columns",
+ },
+ {
+ .name = "azoteq,lower-cal-y",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 1,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "lower vertical calibration",
+ },
+ {
+ .name = "azoteq,lower-cal-x",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "lower horizontal calibration",
+ },
+ {
+ .name = "azoteq,upper-cal-y",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 2,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "upper vertical calibration",
+ },
+ {
+ .name = "azoteq,upper-cal-x",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 2,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "upper horizontal calibration",
+ },
+ {
+ .name = "azoteq,top-speed",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 3,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 4,
+ .label = "top speed",
+ },
+ {
+ .name = "azoteq,bottom-speed",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_offset = 3,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "bottom speed",
+ },
+ {
+ .name = "azoteq,gesture-min-ms",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_key = IQS7222_REG_KEY_TAP,
+ .reg_offset = 20,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "minimum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ .reg_offset = 21,
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_key = IQS7222_REG_KEY_TAP,
+ .reg_offset = 21,
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_pitch = 16,
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_key = IQS7222_REG_KEY_TAP,
+ .reg_offset = 22,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_grp = IQS7222_REG_GRP_TPAD,
+ .reg_key = IQS7222_REG_KEY_AXIAL,
+ .reg_offset = 23,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "gesture distance",
+ },
+ {
+ .name = "drive-open-drain",
+ .reg_grp = IQS7222_REG_GRP_GPIO,
+ .reg_offset = 0,
+ .reg_shift = 1,
+ .reg_width = 1,
+ },
+ {
+ .name = "azoteq,timeout-ati-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 1,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .val_pitch = 500,
+ .label = "ATI error timeout",
+ },
+ {
+ .name = "azoteq,rate-ati-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 2,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "ATI report rate",
+ },
+ {
+ .name = "azoteq,timeout-np-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 3,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "normal-power mode timeout",
+ },
+ {
+ .name = "azoteq,rate-np-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 4,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .val_max = 3000,
+ .label = "normal-power mode report rate",
+ },
+ {
+ .name = "azoteq,timeout-lp-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 5,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "low-power mode timeout",
+ },
+ {
+ .name = "azoteq,rate-lp-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 6,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .val_max = 3000,
+ .label = "low-power mode report rate",
+ },
+ {
+ .name = "azoteq,timeout-ulp-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 7,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .label = "ultra-low-power mode timeout",
+ },
+ {
+ .name = "azoteq,rate-ulp-ms",
+ .reg_grp = IQS7222_REG_GRP_SYS,
+ .reg_offset = 8,
+ .reg_shift = 0,
+ .reg_width = 16,
+ .val_max = 3000,
+ .label = "ultra-low-power mode report rate",
+ },
+};
+
+struct iqs7222_private {
+ const struct iqs7222_dev_desc *dev_desc;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *irq_gpio;
+ struct i2c_client *client;
+ struct input_dev *keypad;
+ struct touchscreen_properties prop;
+ unsigned int kp_type[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+ unsigned int kp_code[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+ unsigned int sl_code[IQS7222_MAX_SLDR][ARRAY_SIZE(iqs7222_sl_events)];
+ unsigned int sl_axis[IQS7222_MAX_SLDR];
+ unsigned int tp_code[ARRAY_SIZE(iqs7222_tp_events)];
+ u16 cycle_setup[IQS7222_MAX_CHAN / 2][IQS7222_MAX_COLS_CYCLE];
+ u16 glbl_setup[IQS7222_MAX_COLS_GLBL];
+ u16 btn_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_BTN];
+ u16 chan_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_CHAN];
+ u16 filt_setup[IQS7222_MAX_COLS_FILT];
+ u16 sldr_setup[IQS7222_MAX_SLDR][IQS7222_MAX_COLS_SLDR];
+ u16 tpad_setup[IQS7222_MAX_COLS_TPAD];
+ u16 gpio_setup[ARRAY_SIZE(iqs7222_gpio_links)][IQS7222_MAX_COLS_GPIO];
+ u16 sys_setup[IQS7222_MAX_COLS_SYS];
+};
+
+static u16 *iqs7222_setup(struct iqs7222_private *iqs7222,
+ enum iqs7222_reg_grp_id reg_grp, int row)
+{
+ switch (reg_grp) {
+ case IQS7222_REG_GRP_CYCLE:
+ return iqs7222->cycle_setup[row];
+
+ case IQS7222_REG_GRP_GLBL:
+ return iqs7222->glbl_setup;
+
+ case IQS7222_REG_GRP_BTN:
+ return iqs7222->btn_setup[row];
+
+ case IQS7222_REG_GRP_CHAN:
+ return iqs7222->chan_setup[row];
+
+ case IQS7222_REG_GRP_FILT:
+ return iqs7222->filt_setup;
+
+ case IQS7222_REG_GRP_SLDR:
+ return iqs7222->sldr_setup[row];
+
+ case IQS7222_REG_GRP_TPAD:
+ return iqs7222->tpad_setup;
+
+ case IQS7222_REG_GRP_GPIO:
+ return iqs7222->gpio_setup[row];
+
+ case IQS7222_REG_GRP_SYS:
+ return iqs7222->sys_setup;
+
+ default:
+ return NULL;
+ }
+}
+
+static int iqs7222_irq_poll(struct iqs7222_private *iqs7222, u16 timeout_ms)
+{
+ ktime_t irq_timeout = ktime_add_ms(ktime_get(), timeout_ms);
+ int ret;
+
+ do {
+ usleep_range(1000, 1100);
+
+ ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return 0;
+ } while (ktime_compare(ktime_get(), irq_timeout) < 0);
+
+ return -EBUSY;
+}
+
+static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
+{
+ struct i2c_client *client = iqs7222->client;
+ int error;
+
+ if (!iqs7222->reset_gpio)
+ return 0;
+
+ gpiod_set_value_cansleep(iqs7222->reset_gpio, 1);
+ usleep_range(1000, 1100);
+
+ gpiod_set_value_cansleep(iqs7222->reset_gpio, 0);
+
+ error = iqs7222_irq_poll(iqs7222, IQS7222_RESET_TIMEOUT_MS);
+ if (error)
+ dev_err(&client->dev, "Failed to reset device: %d\n", error);
+
+ return error;
+}
+
+static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
+{
+ u8 msg_buf[] = { 0xFF, };
+ int ret;
+
+ /*
+ * The device cannot communicate until it asserts its interrupt (RDY)
+ * pin. Attempts to do so while RDY is deasserted return an ACK; how-
+ * ever all write data is ignored, and all read data returns 0xEE.
+ *
+ * Unsolicited communication must be preceded by a special force com-
+ * munication command, after which the device eventually asserts its
+ * RDY pin and agrees to communicate.
+ *
+ * Regardless of whether communication is forced or the result of an
+ * interrupt, the device automatically deasserts its RDY pin once it
+ * detects an I2C stop condition, or a timeout expires.
+ */
+ ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return 0;
+
+ ret = i2c_master_send(iqs7222->client, msg_buf, sizeof(msg_buf));
+ if (ret < (int)sizeof(msg_buf)) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ /*
+ * The datasheet states that the host must wait to retry any
+ * failed attempt to communicate over I2C.
+ */
+ msleep(IQS7222_COMMS_RETRY_MS);
+ return ret;
+ }
+
+ return iqs7222_irq_poll(iqs7222, IQS7222_COMMS_TIMEOUT_MS);
+}
+
+static int iqs7222_read_burst(struct iqs7222_private *iqs7222,
+ u16 reg, void *val, u16 val_len)
+{
+ u8 reg_buf[sizeof(__be16)];
+ int ret, i;
+ struct i2c_client *client = iqs7222->client;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = reg > U8_MAX ? sizeof(reg) : sizeof(u8),
+ .buf = reg_buf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = val_len,
+ .buf = (u8 *)val,
+ },
+ };
+
+ if (reg > U8_MAX)
+ put_unaligned_be16(reg, reg_buf);
+ else
+ *reg_buf = (u8)reg;
+
+ /*
+ * The following loop protects against an edge case in which the RDY
+ * pin is automatically deasserted just as the read is initiated. In
+ * that case, the read must be retried using forced communication.
+ */
+ for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+ ret = iqs7222_force_comms(iqs7222);
+ if (ret < 0)
+ continue;
+
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret < (int)ARRAY_SIZE(msg)) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ msleep(IQS7222_COMMS_RETRY_MS);
+ continue;
+ }
+
+ if (get_unaligned_le16(msg[1].buf) == IQS7222_COMMS_ERROR) {
+ ret = -ENODATA;
+ continue;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ /*
+ * The following delay ensures the device has deasserted the RDY pin
+ * following the I2C stop condition.
+ */
+ usleep_range(50, 100);
+
+ if (ret < 0)
+ dev_err(&client->dev,
+ "Failed to read from address 0x%04X: %d\n", reg, ret);
+
+ return ret;
+}
+
+static int iqs7222_read_word(struct iqs7222_private *iqs7222, u16 reg, u16 *val)
+{
+ __le16 val_buf;
+ int error;
+
+ error = iqs7222_read_burst(iqs7222, reg, &val_buf, sizeof(val_buf));
+ if (error)
+ return error;
+
+ *val = le16_to_cpu(val_buf);
+
+ return 0;
+}
+
+static int iqs7222_write_burst(struct iqs7222_private *iqs7222,
+ u16 reg, const void *val, u16 val_len)
+{
+ int reg_len = reg > U8_MAX ? sizeof(reg) : sizeof(u8);
+ int msg_len = reg_len + val_len;
+ int ret, i;
+ struct i2c_client *client = iqs7222->client;
+ u8 *msg_buf;
+
+ msg_buf = kzalloc(msg_len, GFP_KERNEL);
+ if (!msg_buf)
+ return -ENOMEM;
+
+ if (reg > U8_MAX)
+ put_unaligned_be16(reg, msg_buf);
+ else
+ *msg_buf = (u8)reg;
+
+ memcpy(msg_buf + reg_len, val, val_len);
+
+ /*
+ * The following loop protects against an edge case in which the RDY
+ * pin is automatically asserted just before the force communication
+ * command is sent.
+ *
+ * In that case, the subsequent I2C stop condition tricks the device
+ * into preemptively deasserting the RDY pin and the command must be
+ * sent again.
+ */
+ for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+ ret = iqs7222_force_comms(iqs7222);
+ if (ret < 0)
+ continue;
+
+ ret = i2c_master_send(client, msg_buf, msg_len);
+ if (ret < msg_len) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ msleep(IQS7222_COMMS_RETRY_MS);
+ continue;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ kfree(msg_buf);
+
+ usleep_range(50, 100);
+
+ if (ret < 0)
+ dev_err(&client->dev,
+ "Failed to write to address 0x%04X: %d\n", reg, ret);
+
+ return ret;
+}
+
+static int iqs7222_write_word(struct iqs7222_private *iqs7222, u16 reg, u16 val)
+{
+ __le16 val_buf = cpu_to_le16(val);
+
+ return iqs7222_write_burst(iqs7222, reg, &val_buf, sizeof(val_buf));
+}
+
+static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222)
+{
+ struct i2c_client *client = iqs7222->client;
+ ktime_t ati_timeout;
+ u16 sys_status = 0;
+ u16 sys_setup;
+ int error, i;
+
+ /*
+ * The reserved fields of the system setup register may have changed
+ * as a result of other registers having been written. As such, read
+ * the register's latest value to avoid unexpected behavior when the
+ * register is written in the loop that follows.
+ */
+ error = iqs7222_read_word(iqs7222, IQS7222_SYS_SETUP, &sys_setup);
+ if (error)
+ return error;
+
+ for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+ /*
+ * Trigger ATI from streaming and normal-power modes so that
+ * the RDY pin continues to be asserted during ATI.
+ */
+ error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+ sys_setup |
+ IQS7222_SYS_SETUP_REDO_ATI);
+ if (error)
+ return error;
+
+ ati_timeout = ktime_add_ms(ktime_get(), IQS7222_ATI_TIMEOUT_MS);
+
+ do {
+ error = iqs7222_irq_poll(iqs7222,
+ IQS7222_COMMS_TIMEOUT_MS);
+ if (error)
+ continue;
+
+ error = iqs7222_read_word(iqs7222, IQS7222_SYS_STATUS,
+ &sys_status);
+ if (error)
+ return error;
+
+ if (sys_status & IQS7222_SYS_STATUS_RESET)
+ return 0;
+
+ if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR)
+ break;
+
+ if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE)
+ continue;
+
+ /*
+ * Use stream-in-touch mode if either slider reports
+ * absolute position.
+ */
+ sys_setup |= test_bit(EV_ABS, iqs7222->keypad->evbit)
+ ? IQS7222_SYS_SETUP_INTF_MODE_TOUCH
+ : IQS7222_SYS_SETUP_INTF_MODE_EVENT;
+ sys_setup |= IQS7222_SYS_SETUP_PWR_MODE_AUTO;
+
+ return iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+ sys_setup);
+ } while (ktime_compare(ktime_get(), ati_timeout) < 0);
+
+ dev_err(&client->dev,
+ "ATI attempt %d of %d failed with status 0x%02X, %s\n",
+ i + 1, IQS7222_NUM_RETRIES, (u8)sys_status,
+ i + 1 < IQS7222_NUM_RETRIES ? "retrying" : "stopping");
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ int comms_offset = dev_desc->comms_offset;
+ int error, i, j, k;
+
+ /*
+ * Acknowledge reset before writing any registers in case the device
+ * suffers a spurious reset during initialization.
+ */
+ if (dir == WRITE) {
+ error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+ iqs7222->sys_setup[0] |
+ IQS7222_SYS_SETUP_ACK_RESET);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Take advantage of the stop-bit disable function, if available, to
+ * save the trouble of having to reopen a communication window after
+ * each burst read or write.
+ */
+ if (comms_offset) {
+ u16 comms_setup;
+
+ error = iqs7222_read_word(iqs7222,
+ IQS7222_SYS_SETUP + comms_offset,
+ &comms_setup);
+ if (error)
+ return error;
+
+ error = iqs7222_write_word(iqs7222,
+ IQS7222_SYS_SETUP + comms_offset,
+ comms_setup | IQS7222_COMMS_HOLD);
+ if (error)
+ return error;
+ }
+
+ for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
+ int num_row = dev_desc->reg_grps[i].num_row;
+ int num_col = dev_desc->reg_grps[i].num_col;
+ u16 reg = dev_desc->reg_grps[i].base;
+ __le16 *val_buf;
+ u16 val_len = dev_desc->reg_grps[i].val_len ? : num_col * sizeof(*val_buf);
+ u16 *val;
+
+ if (!num_col)
+ continue;
+
+ val = iqs7222_setup(iqs7222, i, 0);
+ if (!val)
+ continue;
+
+ val_buf = kcalloc(num_col, sizeof(__le16), GFP_KERNEL);
+ if (!val_buf)
+ return -ENOMEM;
+
+ for (j = 0; j < num_row; j++) {
+ switch (dir) {
+ case READ:
+ error = iqs7222_read_burst(iqs7222, reg,
+ val_buf, val_len);
+ for (k = 0; k < num_col; k++)
+ val[k] = le16_to_cpu(val_buf[k]);
+ break;
+
+ case WRITE:
+ for (k = 0; k < num_col; k++)
+ val_buf[k] = cpu_to_le16(val[k]);
+ error = iqs7222_write_burst(iqs7222, reg,
+ val_buf, val_len);
+ break;
+
+ default:
+ error = -EINVAL;
+ }
+
+ if (error)
+ break;
+
+ reg += IQS7222_REG_OFFSET;
+ val += iqs7222_max_cols[i];
+ }
+
+ kfree(val_buf);
+
+ if (error)
+ return error;
+ }
+
+ if (comms_offset) {
+ u16 comms_setup;
+
+ error = iqs7222_read_word(iqs7222,
+ IQS7222_SYS_SETUP + comms_offset,
+ &comms_setup);
+ if (error)
+ return error;
+
+ error = iqs7222_write_word(iqs7222,
+ IQS7222_SYS_SETUP + comms_offset,
+ comms_setup & ~IQS7222_COMMS_HOLD);
+ if (error)
+ return error;
+ }
+
+ if (dir == READ) {
+ iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK;
+ iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK;
+ return 0;
+ }
+
+ return iqs7222_ati_trigger(iqs7222);
+}
+
+static int iqs7222_dev_info(struct iqs7222_private *iqs7222)
+{
+ struct i2c_client *client = iqs7222->client;
+ bool prod_num_valid = false;
+ __le16 dev_id[3];
+ int error, i;
+
+ error = iqs7222_read_burst(iqs7222, IQS7222_PROD_NUM, dev_id,
+ sizeof(dev_id));
+ if (error)
+ return error;
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_devs); i++) {
+ if (le16_to_cpu(dev_id[0]) != iqs7222_devs[i].prod_num)
+ continue;
+
+ prod_num_valid = true;
+
+ if (le16_to_cpu(dev_id[1]) < iqs7222_devs[i].fw_major)
+ continue;
+
+ if (le16_to_cpu(dev_id[2]) < iqs7222_devs[i].fw_minor)
+ continue;
+
+ iqs7222->dev_desc = &iqs7222_devs[i];
+ return 0;
+ }
+
+ if (prod_num_valid)
+ dev_err(&client->dev, "Unsupported firmware revision: %u.%u\n",
+ le16_to_cpu(dev_id[1]), le16_to_cpu(dev_id[2]));
+ else
+ dev_err(&client->dev, "Unrecognized product number: %u\n",
+ le16_to_cpu(dev_id[0]));
+
+ return -EINVAL;
+}
+
+static int iqs7222_gpio_select(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *child_node,
+ int child_enable, u16 child_link)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ struct i2c_client *client = iqs7222->client;
+ int num_gpio = dev_desc->reg_grps[IQS7222_REG_GRP_GPIO].num_row;
+ int error, count, i;
+ unsigned int gpio_sel[ARRAY_SIZE(iqs7222_gpio_links)];
+
+ if (!num_gpio)
+ return 0;
+
+ if (!fwnode_property_present(child_node, "azoteq,gpio-select"))
+ return 0;
+
+ count = fwnode_property_count_u32(child_node, "azoteq,gpio-select");
+ if (count > num_gpio) {
+ dev_err(&client->dev, "Invalid number of %s GPIOs\n",
+ fwnode_get_name(child_node));
+ return -EINVAL;
+ } else if (count < 0) {
+ dev_err(&client->dev, "Failed to count %s GPIOs: %d\n",
+ fwnode_get_name(child_node), count);
+ return count;
+ }
+
+ error = fwnode_property_read_u32_array(child_node,
+ "azoteq,gpio-select",
+ gpio_sel, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read %s GPIOs: %d\n",
+ fwnode_get_name(child_node), error);
+ return error;
+ }
+
+ for (i = 0; i < count; i++) {
+ u16 *gpio_setup;
+
+ if (gpio_sel[i] >= num_gpio) {
+ dev_err(&client->dev, "Invalid %s GPIO: %u\n",
+ fwnode_get_name(child_node), gpio_sel[i]);
+ return -EINVAL;
+ }
+
+ gpio_setup = iqs7222->gpio_setup[gpio_sel[i]];
+
+ if (gpio_setup[2] && child_link != gpio_setup[2]) {
+ dev_err(&client->dev,
+ "Conflicting GPIO %u event types\n",
+ gpio_sel[i]);
+ return -EINVAL;
+ }
+
+ gpio_setup[0] |= IQS7222_GPIO_SETUP_0_GPIO_EN;
+ gpio_setup[1] |= child_enable;
+ gpio_setup[2] = child_link;
+ }
+
+ return 0;
+}
+
+static int iqs7222_parse_props(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *reg_grp_node,
+ int reg_grp_index,
+ enum iqs7222_reg_grp_id reg_grp,
+ enum iqs7222_reg_key_id reg_key)
+{
+ u16 *setup = iqs7222_setup(iqs7222, reg_grp, reg_grp_index);
+ struct i2c_client *client = iqs7222->client;
+ int i;
+
+ if (!setup)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_props); i++) {
+ const char *name = iqs7222_props[i].name;
+ int reg_offset = iqs7222_props[i].reg_offset;
+ int reg_shift = iqs7222_props[i].reg_shift;
+ int reg_width = iqs7222_props[i].reg_width;
+ int val_pitch = iqs7222_props[i].val_pitch ? : 1;
+ int val_min = iqs7222_props[i].val_min;
+ int val_max = iqs7222_props[i].val_max;
+ bool invert = iqs7222_props[i].invert;
+ const char *label = iqs7222_props[i].label ? : name;
+ unsigned int val;
+ int error;
+
+ if (iqs7222_props[i].reg_grp != reg_grp ||
+ iqs7222_props[i].reg_key != reg_key)
+ continue;
+
+ /*
+ * Boolean register fields are one bit wide; they are forcibly
+ * reset to provide a means to undo changes by a bootloader if
+ * necessary.
+ *
+ * Scalar fields, on the other hand, are left untouched unless
+ * their corresponding properties are present.
+ */
+ if (reg_width == 1) {
+ if (invert)
+ setup[reg_offset] |= BIT(reg_shift);
+ else
+ setup[reg_offset] &= ~BIT(reg_shift);
+ }
+
+ if (!fwnode_property_present(reg_grp_node, name))
+ continue;
+
+ if (reg_width == 1) {
+ if (invert)
+ setup[reg_offset] &= ~BIT(reg_shift);
+ else
+ setup[reg_offset] |= BIT(reg_shift);
+
+ continue;
+ }
+
+ error = fwnode_property_read_u32(reg_grp_node, name, &val);
+ if (error) {
+ dev_err(&client->dev, "Failed to read %s %s: %d\n",
+ fwnode_get_name(reg_grp_node), label, error);
+ return error;
+ }
+
+ if (!val_max)
+ val_max = GENMASK(reg_width - 1, 0) * val_pitch;
+
+ if (val < val_min || val > val_max) {
+ dev_err(&client->dev, "Invalid %s %s: %u\n",
+ fwnode_get_name(reg_grp_node), label, val);
+ return -EINVAL;
+ }
+
+ setup[reg_offset] &= ~GENMASK(reg_shift + reg_width - 1,
+ reg_shift);
+ setup[reg_offset] |= (val / val_pitch << reg_shift);
+ }
+
+ return 0;
+}
+
+static int iqs7222_parse_event(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *event_node,
+ int reg_grp_index,
+ enum iqs7222_reg_grp_id reg_grp,
+ enum iqs7222_reg_key_id reg_key,
+ u16 event_enable, u16 event_link,
+ unsigned int *event_type,
+ unsigned int *event_code)
+{
+ struct i2c_client *client = iqs7222->client;
+ int error;
+
+ error = iqs7222_parse_props(iqs7222, event_node, reg_grp_index,
+ reg_grp, reg_key);
+ if (error)
+ return error;
+
+ error = iqs7222_gpio_select(iqs7222, event_node, event_enable,
+ event_link);
+ if (error)
+ return error;
+
+ error = fwnode_property_read_u32(event_node, "linux,code", event_code);
+ if (error == -EINVAL) {
+ return 0;
+ } else if (error) {
+ dev_err(&client->dev, "Failed to read %s code: %d\n",
+ fwnode_get_name(event_node), error);
+ return error;
+ }
+
+ if (!event_type) {
+ input_set_capability(iqs7222->keypad, EV_KEY, *event_code);
+ return 0;
+ }
+
+ error = fwnode_property_read_u32(event_node, "linux,input-type",
+ event_type);
+ if (error == -EINVAL) {
+ *event_type = EV_KEY;
+ } else if (error) {
+ dev_err(&client->dev, "Failed to read %s input type: %d\n",
+ fwnode_get_name(event_node), error);
+ return error;
+ } else if (*event_type != EV_KEY && *event_type != EV_SW) {
+ dev_err(&client->dev, "Invalid %s input type: %d\n",
+ fwnode_get_name(event_node), *event_type);
+ return -EINVAL;
+ }
+
+ input_set_capability(iqs7222->keypad, *event_type, *event_code);
+
+ return 0;
+}
+
+static int iqs7222_parse_cycle(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *cycle_node, int cycle_index)
+{
+ u16 *cycle_setup = iqs7222->cycle_setup[cycle_index];
+ struct i2c_client *client = iqs7222->client;
+ unsigned int pins[9];
+ int error, count, i;
+
+ /*
+ * Each channel shares a cycle with one other channel; the mapping of
+ * channels to cycles is fixed. Properties defined for a cycle impact
+ * both channels tied to the cycle.
+ *
+ * Unlike channels which are restricted to a select range of CRx pins
+ * based on channel number, any cycle can claim any of the device's 9
+ * CTx pins (CTx0-8).
+ */
+ if (!fwnode_property_present(cycle_node, "azoteq,tx-enable"))
+ return 0;
+
+ count = fwnode_property_count_u32(cycle_node, "azoteq,tx-enable");
+ if (count < 0) {
+ dev_err(&client->dev, "Failed to count %s CTx pins: %d\n",
+ fwnode_get_name(cycle_node), count);
+ return count;
+ } else if (count > ARRAY_SIZE(pins)) {
+ dev_err(&client->dev, "Invalid number of %s CTx pins\n",
+ fwnode_get_name(cycle_node));
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(cycle_node, "azoteq,tx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read %s CTx pins: %d\n",
+ fwnode_get_name(cycle_node), error);
+ return error;
+ }
+
+ cycle_setup[1] &= ~GENMASK(7 + ARRAY_SIZE(pins) - 1, 7);
+
+ for (i = 0; i < count; i++) {
+ if (pins[i] > 8) {
+ dev_err(&client->dev, "Invalid %s CTx pin: %u\n",
+ fwnode_get_name(cycle_node), pins[i]);
+ return -EINVAL;
+ }
+
+ cycle_setup[1] |= BIT(pins[i] + 7);
+ }
+
+ return 0;
+}
+
+static int iqs7222_parse_chan(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *chan_node, int chan_index)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ struct i2c_client *client = iqs7222->client;
+ int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+ int ext_chan = dev_desc->ext_chan ? : num_chan;
+ int error, i;
+ u16 *chan_setup = iqs7222->chan_setup[chan_index];
+ u16 *sys_setup = iqs7222->sys_setup;
+ unsigned int val;
+
+ if (dev_desc->allow_offset &&
+ fwnode_property_present(chan_node, "azoteq,ulp-allow"))
+ sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
+
+ chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+ /*
+ * The reference channel function allows for differential measurements
+ * and is only available in the case of IQS7222A or IQS7222C.
+ */
+ if (dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_col > 4 &&
+ fwnode_property_present(chan_node, "azoteq,ref-select")) {
+ u16 *ref_setup;
+
+ error = fwnode_property_read_u32(chan_node, "azoteq,ref-select",
+ &val);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read %s reference channel: %d\n",
+ fwnode_get_name(chan_node), error);
+ return error;
+ }
+
+ if (val >= ext_chan) {
+ dev_err(&client->dev,
+ "Invalid %s reference channel: %u\n",
+ fwnode_get_name(chan_node), val);
+ return -EINVAL;
+ }
+
+ ref_setup = iqs7222->chan_setup[val];
+
+ /*
+ * Configure the current channel as a follower of the selected
+ * reference channel.
+ */
+ chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW;
+ chan_setup[4] = val * 42 + 1048;
+
+ error = fwnode_property_read_u32(chan_node, "azoteq,ref-weight",
+ &val);
+ if (!error) {
+ if (val > U16_MAX) {
+ dev_err(&client->dev,
+ "Invalid %s reference weight: %u\n",
+ fwnode_get_name(chan_node), val);
+ return -EINVAL;
+ }
+
+ chan_setup[5] = val;
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev,
+ "Failed to read %s reference weight: %d\n",
+ fwnode_get_name(chan_node), error);
+ return error;
+ }
+
+ /*
+ * Configure the selected channel as a reference channel which
+ * serves the current channel.
+ */
+ ref_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
+ ref_setup[5] |= BIT(chan_index);
+
+ ref_setup[4] = dev_desc->touch_link;
+ if (fwnode_property_present(chan_node, "azoteq,use-prox"))
+ ref_setup[4] -= 2;
+ } else if (dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row &&
+ fwnode_property_present(chan_node,
+ "azoteq,counts-filt-enable")) {
+ /*
+ * In the case of IQS7222D, however, the reference mode field
+ * is partially repurposed as a counts filter enable control.
+ */
+ chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
+ }
+
+ if (fwnode_property_present(chan_node, "azoteq,rx-enable")) {
+ /*
+ * Each channel can claim up to 4 CRx pins. The first half of
+ * the channels can use CRx0-3, while the second half can use
+ * CRx4-7.
+ */
+ unsigned int pins[4];
+ int count;
+
+ count = fwnode_property_count_u32(chan_node,
+ "azoteq,rx-enable");
+ if (count < 0) {
+ dev_err(&client->dev,
+ "Failed to count %s CRx pins: %d\n",
+ fwnode_get_name(chan_node), count);
+ return count;
+ } else if (count > ARRAY_SIZE(pins)) {
+ dev_err(&client->dev,
+ "Invalid number of %s CRx pins\n",
+ fwnode_get_name(chan_node));
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(chan_node,
+ "azoteq,rx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read %s CRx pins: %d\n",
+ fwnode_get_name(chan_node), error);
+ return error;
+ }
+
+ chan_setup[0] &= ~GENMASK(4 + ARRAY_SIZE(pins) - 1, 4);
+
+ for (i = 0; i < count; i++) {
+ int min_crx = chan_index < ext_chan / 2 ? 0 : 4;
+
+ if (pins[i] < min_crx || pins[i] > min_crx + 3) {
+ dev_err(&client->dev,
+ "Invalid %s CRx pin: %u\n",
+ fwnode_get_name(chan_node), pins[i]);
+ return -EINVAL;
+ }
+
+ chan_setup[0] |= BIT(pins[i] + 4 - min_crx);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_kp_events); i++) {
+ const char *event_name = iqs7222_kp_events[i].name;
+ u16 event_enable = iqs7222_kp_events[i].enable;
+
+ struct fwnode_handle *event_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(chan_node, event_name);
+ if (!event_node)
+ continue;
+
+ error = fwnode_property_read_u32(event_node,
+ "azoteq,timeout-press-ms",
+ &val);
+ if (!error) {
+ /*
+ * The IQS7222B employs a global pair of press timeout
+ * registers as opposed to channel-specific registers.
+ */
+ u16 *setup = dev_desc->reg_grps
+ [IQS7222_REG_GRP_BTN].num_col > 2 ?
+ &iqs7222->btn_setup[chan_index][2] :
+ &sys_setup[9];
+
+ if (val > U8_MAX * 500) {
+ dev_err(&client->dev,
+ "Invalid %s press timeout: %u\n",
+ fwnode_get_name(event_node), val);
+ return -EINVAL;
+ }
+
+ *setup &= ~(U8_MAX << i * 8);
+ *setup |= (val / 500 << i * 8);
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev,
+ "Failed to read %s press timeout: %d\n",
+ fwnode_get_name(event_node), error);
+ return error;
+ }
+
+ error = iqs7222_parse_event(iqs7222, event_node, chan_index,
+ IQS7222_REG_GRP_BTN,
+ iqs7222_kp_events[i].reg_key,
+ BIT(chan_index),
+ dev_desc->touch_link - (i ? 0 : 2),
+ &iqs7222->kp_type[chan_index][i],
+ &iqs7222->kp_code[chan_index][i]);
+ if (error)
+ return error;
+
+ if (!iqs7222->kp_type[chan_index][i])
+ continue;
+
+ if (!dev_desc->event_offset)
+ continue;
+
+ sys_setup[dev_desc->event_offset] |= event_enable;
+ }
+
+ /*
+ * The following call handles a special pair of properties that apply
+ * to a channel node, but reside within the button (event) group.
+ */
+ return iqs7222_parse_props(iqs7222, chan_node, chan_index,
+ IQS7222_REG_GRP_BTN,
+ IQS7222_REG_KEY_DEBOUNCE);
+}
+
+static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *sldr_node, int sldr_index)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ struct i2c_client *client = iqs7222->client;
+ int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+ int ext_chan = dev_desc->ext_chan ? : num_chan;
+ int count, error, reg_offset, i;
+ u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset];
+ u16 *sldr_setup = iqs7222->sldr_setup[sldr_index];
+ unsigned int chan_sel[4], val;
+
+ /*
+ * Each slider can be spread across 3 to 4 channels. It is possible to
+ * select only 2 channels, but doing so prevents the slider from using
+ * the specified resolution.
+ */
+ count = fwnode_property_count_u32(sldr_node, "azoteq,channel-select");
+ if (count < 0) {
+ dev_err(&client->dev, "Failed to count %s channels: %d\n",
+ fwnode_get_name(sldr_node), count);
+ return count;
+ } else if (count < 3 || count > ARRAY_SIZE(chan_sel)) {
+ dev_err(&client->dev, "Invalid number of %s channels\n",
+ fwnode_get_name(sldr_node));
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(sldr_node,
+ "azoteq,channel-select",
+ chan_sel, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read %s channels: %d\n",
+ fwnode_get_name(sldr_node), error);
+ return error;
+ }
+
+ /*
+ * Resolution and top speed, if small enough, are packed into a single
+ * register. Otherwise, each occupies its own register and the rest of
+ * the slider-related register addresses are offset by one.
+ */
+ reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1;
+
+ sldr_setup[0] |= count;
+ sldr_setup[3 + reg_offset] &= ~GENMASK(ext_chan - 1, 0);
+
+ for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
+ sldr_setup[5 + reg_offset + i] = 0;
+ if (i >= count)
+ continue;
+
+ if (chan_sel[i] >= ext_chan) {
+ dev_err(&client->dev, "Invalid %s channel: %u\n",
+ fwnode_get_name(sldr_node), chan_sel[i]);
+ return -EINVAL;
+ }
+
+ /*
+ * The following fields indicate which channels participate in
+ * the slider, as well as each channel's relative placement.
+ */
+ sldr_setup[3 + reg_offset] |= BIT(chan_sel[i]);
+ sldr_setup[5 + reg_offset + i] = chan_sel[i] * 42 + 1080;
+ }
+
+ sldr_setup[4 + reg_offset] = dev_desc->touch_link;
+ if (fwnode_property_present(sldr_node, "azoteq,use-prox"))
+ sldr_setup[4 + reg_offset] -= 2;
+
+ error = fwnode_property_read_u32(sldr_node, "azoteq,slider-size", &val);
+ if (!error) {
+ if (val > dev_desc->sldr_res) {
+ dev_err(&client->dev, "Invalid %s size: %u\n",
+ fwnode_get_name(sldr_node), val);
+ return -EINVAL;
+ }
+
+ if (reg_offset) {
+ sldr_setup[3] = val;
+ } else {
+ sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_RES_MASK;
+ sldr_setup[2] |= (val / 16 <<
+ IQS7222_SLDR_SETUP_2_RES_SHIFT);
+ }
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev, "Failed to read %s size: %d\n",
+ fwnode_get_name(sldr_node), error);
+ return error;
+ }
+
+ if (!(reg_offset ? sldr_setup[3]
+ : sldr_setup[2] & IQS7222_SLDR_SETUP_2_RES_MASK)) {
+ dev_err(&client->dev, "Undefined %s size\n",
+ fwnode_get_name(sldr_node));
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32(sldr_node, "azoteq,top-speed", &val);
+ if (!error) {
+ if (val > (reg_offset ? U16_MAX : U8_MAX * 4)) {
+ dev_err(&client->dev, "Invalid %s top speed: %u\n",
+ fwnode_get_name(sldr_node), val);
+ return -EINVAL;
+ }
+
+ if (reg_offset) {
+ sldr_setup[2] = val;
+ } else {
+ sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK;
+ sldr_setup[2] |= (val / 4);
+ }
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev, "Failed to read %s top speed: %d\n",
+ fwnode_get_name(sldr_node), error);
+ return error;
+ }
+
+ error = fwnode_property_read_u32(sldr_node, "linux,axis", &val);
+ if (!error) {
+ u16 sldr_max = sldr_setup[3] - 1;
+
+ if (!reg_offset) {
+ sldr_max = sldr_setup[2];
+
+ sldr_max &= IQS7222_SLDR_SETUP_2_RES_MASK;
+ sldr_max >>= IQS7222_SLDR_SETUP_2_RES_SHIFT;
+
+ sldr_max = sldr_max * 16 - 1;
+ }
+
+ input_set_abs_params(iqs7222->keypad, val, 0, sldr_max, 0, 0);
+ iqs7222->sl_axis[sldr_index] = val;
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev, "Failed to read %s axis: %d\n",
+ fwnode_get_name(sldr_node), error);
+ return error;
+ }
+
+ if (dev_desc->wheel_enable) {
+ sldr_setup[0] &= ~dev_desc->wheel_enable;
+ if (iqs7222->sl_axis[sldr_index] == ABS_WHEEL)
+ sldr_setup[0] |= dev_desc->wheel_enable;
+ }
+
+ /*
+ * The absence of a register offset makes it safe to assume the device
+ * supports gestures, each of which is first disabled until explicitly
+ * enabled.
+ */
+ if (!reg_offset)
+ for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++)
+ sldr_setup[9] &= ~iqs7222_sl_events[i].enable;
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) {
+ const char *event_name = iqs7222_sl_events[i].name;
+ enum iqs7222_reg_key_id reg_key;
+
+ struct fwnode_handle *event_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(sldr_node, event_name);
+ if (!event_node)
+ continue;
+
+ /*
+ * Depending on the device, gestures are either offered using
+ * one of two timing resolutions, or are not supported at all.
+ */
+ if (reg_offset)
+ reg_key = IQS7222_REG_KEY_RESERVED;
+ else if (dev_desc->legacy_gesture &&
+ iqs7222_sl_events[i].reg_key == IQS7222_REG_KEY_TAP)
+ reg_key = IQS7222_REG_KEY_TAP_LEGACY;
+ else if (dev_desc->legacy_gesture &&
+ iqs7222_sl_events[i].reg_key == IQS7222_REG_KEY_AXIAL)
+ reg_key = IQS7222_REG_KEY_AXIAL_LEGACY;
+ else
+ reg_key = iqs7222_sl_events[i].reg_key;
+
+ /*
+ * The press/release event does not expose a direct GPIO link,
+ * but one can be emulated by tying each of the participating
+ * channels to the same GPIO.
+ */
+ error = iqs7222_parse_event(iqs7222, event_node, sldr_index,
+ IQS7222_REG_GRP_SLDR, reg_key,
+ i ? iqs7222_sl_events[i].enable
+ : sldr_setup[3 + reg_offset],
+ i ? 1568 + sldr_index * 30
+ : sldr_setup[4 + reg_offset],
+ NULL,
+ &iqs7222->sl_code[sldr_index][i]);
+ if (error)
+ return error;
+
+ if (!reg_offset)
+ sldr_setup[9] |= iqs7222_sl_events[i].enable;
+
+ if (!dev_desc->event_offset)
+ continue;
+
+ /*
+ * The press/release event is determined based on whether the
+ * coordinate field reports 0xFFFF and solely relies on touch
+ * or proximity interrupts to be unmasked.
+ */
+ if (i && !reg_offset)
+ *event_mask |= (IQS7222_EVENT_MASK_SLDR << sldr_index);
+ else if (sldr_setup[4 + reg_offset] == dev_desc->touch_link)
+ *event_mask |= IQS7222_EVENT_MASK_TOUCH;
+ else
+ *event_mask |= IQS7222_EVENT_MASK_PROX;
+ }
+
+ /*
+ * The following call handles a special pair of properties that shift
+ * to make room for a wheel enable control in the case of IQS7222C.
+ */
+ return iqs7222_parse_props(iqs7222, sldr_node, sldr_index,
+ IQS7222_REG_GRP_SLDR,
+ dev_desc->wheel_enable ?
+ IQS7222_REG_KEY_WHEEL :
+ IQS7222_REG_KEY_NO_WHEEL);
+}
+
+static int iqs7222_parse_tpad(struct iqs7222_private *iqs7222,
+ struct fwnode_handle *tpad_node, int tpad_index)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ struct touchscreen_properties *prop = &iqs7222->prop;
+ struct i2c_client *client = iqs7222->client;
+ int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+ int count, error, i;
+ u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset];
+ u16 *tpad_setup = iqs7222->tpad_setup;
+ unsigned int chan_sel[12];
+
+ error = iqs7222_parse_props(iqs7222, tpad_node, tpad_index,
+ IQS7222_REG_GRP_TPAD,
+ IQS7222_REG_KEY_NONE);
+ if (error)
+ return error;
+
+ count = fwnode_property_count_u32(tpad_node, "azoteq,channel-select");
+ if (count < 0) {
+ dev_err(&client->dev, "Failed to count %s channels: %d\n",
+ fwnode_get_name(tpad_node), count);
+ return count;
+ } else if (!count || count > ARRAY_SIZE(chan_sel)) {
+ dev_err(&client->dev, "Invalid number of %s channels\n",
+ fwnode_get_name(tpad_node));
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(tpad_node,
+ "azoteq,channel-select",
+ chan_sel, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read %s channels: %d\n",
+ fwnode_get_name(tpad_node), error);
+ return error;
+ }
+
+ tpad_setup[6] &= ~GENMASK(num_chan - 1, 0);
+
+ for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
+ tpad_setup[8 + i] = 0;
+ if (i >= count || chan_sel[i] == U8_MAX)
+ continue;
+
+ if (chan_sel[i] >= num_chan) {
+ dev_err(&client->dev, "Invalid %s channel: %u\n",
+ fwnode_get_name(tpad_node), chan_sel[i]);
+ return -EINVAL;
+ }
+
+ /*
+ * The following fields indicate which channels participate in
+ * the trackpad, as well as each channel's relative placement.
+ */
+ tpad_setup[6] |= BIT(chan_sel[i]);
+ tpad_setup[8 + i] = chan_sel[i] * 34 + 1072;
+ }
+
+ tpad_setup[7] = dev_desc->touch_link;
+ if (fwnode_property_present(tpad_node, "azoteq,use-prox"))
+ tpad_setup[7] -= 2;
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++)
+ tpad_setup[20] &= ~(iqs7222_tp_events[i].strict |
+ iqs7222_tp_events[i].enable);
+
+ for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++) {
+ const char *event_name = iqs7222_tp_events[i].name;
+
+ struct fwnode_handle *event_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(tpad_node, event_name);
+ if (!event_node)
+ continue;
+
+ if (fwnode_property_present(event_node,
+ "azoteq,gesture-angle-tighten"))
+ tpad_setup[20] |= iqs7222_tp_events[i].strict;
+
+ tpad_setup[20] |= iqs7222_tp_events[i].enable;
+
+ error = iqs7222_parse_event(iqs7222, event_node, tpad_index,
+ IQS7222_REG_GRP_TPAD,
+ iqs7222_tp_events[i].reg_key,
+ iqs7222_tp_events[i].link, 1566,
+ NULL,
+ &iqs7222->tp_code[i]);
+ if (error)
+ return error;
+
+ if (!dev_desc->event_offset)
+ continue;
+
+ /*
+ * The press/release event is determined based on whether the
+ * coordinate fields report 0xFFFF and solely relies on touch
+ * or proximity interrupts to be unmasked.
+ */
+ if (i)
+ *event_mask |= IQS7222_EVENT_MASK_TPAD;
+ else if (tpad_setup[7] == dev_desc->touch_link)
+ *event_mask |= IQS7222_EVENT_MASK_TOUCH;
+ else
+ *event_mask |= IQS7222_EVENT_MASK_PROX;
+ }
+
+ if (!iqs7222->tp_code[0])
+ return 0;
+
+ input_set_abs_params(iqs7222->keypad, ABS_X,
+ 0, (tpad_setup[4] ? : 1) - 1, 0, 0);
+
+ input_set_abs_params(iqs7222->keypad, ABS_Y,
+ 0, (tpad_setup[5] ? : 1) - 1, 0, 0);
+
+ touchscreen_parse_properties(iqs7222->keypad, false, prop);
+
+ if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
+ dev_err(&client->dev, "Invalid trackpad size: %u*%u\n",
+ prop->max_x, prop->max_y);
+ return -EINVAL;
+ }
+
+ tpad_setup[4] = prop->max_x + 1;
+ tpad_setup[5] = prop->max_y + 1;
+
+ return 0;
+}
+
+static int (*iqs7222_parse_extra[IQS7222_NUM_REG_GRPS])
+ (struct iqs7222_private *iqs7222,
+ struct fwnode_handle *reg_grp_node,
+ int reg_grp_index) = {
+ [IQS7222_REG_GRP_CYCLE] = iqs7222_parse_cycle,
+ [IQS7222_REG_GRP_CHAN] = iqs7222_parse_chan,
+ [IQS7222_REG_GRP_SLDR] = iqs7222_parse_sldr,
+ [IQS7222_REG_GRP_TPAD] = iqs7222_parse_tpad,
+};
+
+static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222,
+ enum iqs7222_reg_grp_id reg_grp,
+ int reg_grp_index)
+{
+ struct i2c_client *client = iqs7222->client;
+ int error;
+
+ struct fwnode_handle *reg_grp_node __free(fwnode_handle) = NULL;
+ if (iqs7222_reg_grp_names[reg_grp]) {
+ char reg_grp_name[16];
+
+ snprintf(reg_grp_name, sizeof(reg_grp_name),
+ iqs7222_reg_grp_names[reg_grp], reg_grp_index);
+
+ reg_grp_node = device_get_named_child_node(&client->dev,
+ reg_grp_name);
+ } else {
+ reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev));
+ }
+
+ if (!reg_grp_node)
+ return 0;
+
+ error = iqs7222_parse_props(iqs7222, reg_grp_node, reg_grp_index,
+ reg_grp, IQS7222_REG_KEY_NONE);
+ if (error)
+ return error;
+
+ if (iqs7222_parse_extra[reg_grp]) {
+ error = iqs7222_parse_extra[reg_grp](iqs7222, reg_grp_node,
+ reg_grp_index);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ const struct iqs7222_reg_grp_desc *reg_grps = dev_desc->reg_grps;
+ u16 *sys_setup = iqs7222->sys_setup;
+ int error, i, j;
+
+ if (dev_desc->allow_offset)
+ sys_setup[dev_desc->allow_offset] = U16_MAX;
+
+ if (dev_desc->event_offset)
+ sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
+
+ for (i = 0; i < reg_grps[IQS7222_REG_GRP_GPIO].num_row; i++) {
+ u16 *gpio_setup = iqs7222->gpio_setup[i];
+
+ gpio_setup[0] &= ~IQS7222_GPIO_SETUP_0_GPIO_EN;
+ gpio_setup[1] = 0;
+ gpio_setup[2] = 0;
+
+ if (reg_grps[IQS7222_REG_GRP_GPIO].num_row == 1)
+ continue;
+
+ /*
+ * The IQS7222C and IQS7222D expose multiple GPIO and must be
+ * informed as to which GPIO this group represents.
+ */
+ for (j = 0; j < ARRAY_SIZE(iqs7222_gpio_links); j++)
+ gpio_setup[0] &= ~BIT(iqs7222_gpio_links[j]);
+
+ gpio_setup[0] |= BIT(iqs7222_gpio_links[i]);
+ }
+
+ for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
+ u16 *chan_setup = iqs7222->chan_setup[i];
+
+ chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_REF_MODE_MASK;
+ chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+ chan_setup[5] = 0;
+ }
+
+ for (i = 0; i < reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+ u16 *sldr_setup = iqs7222->sldr_setup[i];
+
+ sldr_setup[0] &= ~IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK;
+ }
+
+ for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
+ for (j = 0; j < reg_grps[i].num_row; j++) {
+ error = iqs7222_parse_reg_grp(iqs7222, i, j);
+ if (error)
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static int iqs7222_report(struct iqs7222_private *iqs7222)
+{
+ const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+ struct i2c_client *client = iqs7222->client;
+ int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+ int num_stat = dev_desc->reg_grps[IQS7222_REG_GRP_STAT].num_col;
+ int error, i, j;
+ __le16 status[IQS7222_MAX_COLS_STAT];
+
+ error = iqs7222_read_burst(iqs7222, IQS7222_SYS_STATUS, status,
+ num_stat * sizeof(*status));
+ if (error)
+ return error;
+
+ if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_RESET) {
+ dev_err(&client->dev, "Unexpected device reset\n");
+ return iqs7222_dev_init(iqs7222, WRITE);
+ }
+
+ if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ERROR) {
+ dev_err(&client->dev, "Unexpected ATI error\n");
+ return iqs7222_ati_trigger(iqs7222);
+ }
+
+ if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ACTIVE)
+ return 0;
+
+ for (i = 0; i < num_chan; i++) {
+ u16 *chan_setup = iqs7222->chan_setup[i];
+
+ if (!(chan_setup[0] & IQS7222_CHAN_SETUP_0_CHAN_EN))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(iqs7222_kp_events); j++) {
+ /*
+ * Proximity state begins at offset 2 and spills into
+ * offset 3 for devices with more than 16 channels.
+ *
+ * Touch state begins at the first offset immediately
+ * following proximity state.
+ */
+ int k = 2 + j * (num_chan > 16 ? 2 : 1);
+ u16 state = le16_to_cpu(status[k + i / 16]);
+
+ if (!iqs7222->kp_type[i][j])
+ continue;
+
+ input_event(iqs7222->keypad,
+ iqs7222->kp_type[i][j],
+ iqs7222->kp_code[i][j],
+ !!(state & BIT(i % 16)));
+ }
+ }
+
+ for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+ u16 *sldr_setup = iqs7222->sldr_setup[i];
+ u16 sldr_pos = le16_to_cpu(status[4 + i]);
+ u16 state = le16_to_cpu(status[6 + i]);
+
+ if (!(sldr_setup[0] & IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK))
+ continue;
+
+ if (sldr_pos < dev_desc->sldr_res)
+ input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i],
+ sldr_pos);
+
+ input_report_key(iqs7222->keypad, iqs7222->sl_code[i][0],
+ sldr_pos < dev_desc->sldr_res);
+
+ /*
+ * A maximum resolution indicates the device does not support
+ * gestures, in which case the remaining fields are ignored.
+ */
+ if (dev_desc->sldr_res == U16_MAX)
+ continue;
+
+ if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_SLDR << i))
+ continue;
+
+ /*
+ * Skip the press/release event, as it does not have separate
+ * status fields and is handled separately.
+ */
+ for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) {
+ u16 mask = iqs7222_sl_events[j].mask;
+ u16 val = iqs7222_sl_events[j].val;
+
+ input_report_key(iqs7222->keypad,
+ iqs7222->sl_code[i][j],
+ (state & mask) == val);
+ }
+
+ input_sync(iqs7222->keypad);
+
+ for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++)
+ input_report_key(iqs7222->keypad,
+ iqs7222->sl_code[i][j], 0);
+ }
+
+ for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row; i++) {
+ u16 tpad_pos_x = le16_to_cpu(status[4]);
+ u16 tpad_pos_y = le16_to_cpu(status[5]);
+ u16 state = le16_to_cpu(status[6]);
+
+ input_report_key(iqs7222->keypad, iqs7222->tp_code[0],
+ tpad_pos_x < U16_MAX);
+
+ if (tpad_pos_x < U16_MAX)
+ touchscreen_report_pos(iqs7222->keypad, &iqs7222->prop,
+ tpad_pos_x, tpad_pos_y, false);
+
+ if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_TPAD))
+ continue;
+
+ /*
+ * Skip the press/release event, as it does not have separate
+ * status fields and is handled separately.
+ */
+ for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++) {
+ u16 mask = iqs7222_tp_events[j].mask;
+ u16 val = iqs7222_tp_events[j].val;
+
+ input_report_key(iqs7222->keypad,
+ iqs7222->tp_code[j],
+ (state & mask) == val);
+ }
+
+ input_sync(iqs7222->keypad);
+
+ for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++)
+ input_report_key(iqs7222->keypad,
+ iqs7222->tp_code[j], 0);
+ }
+
+ input_sync(iqs7222->keypad);
+
+ return 0;
+}
+
+static irqreturn_t iqs7222_irq(int irq, void *context)
+{
+ struct iqs7222_private *iqs7222 = context;
+
+ return iqs7222_report(iqs7222) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int iqs7222_probe(struct i2c_client *client)
+{
+ struct iqs7222_private *iqs7222;
+ unsigned long irq_flags;
+ int error, irq;
+
+ iqs7222 = devm_kzalloc(&client->dev, sizeof(*iqs7222), GFP_KERNEL);
+ if (!iqs7222)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, iqs7222);
+ iqs7222->client = client;
+
+ iqs7222->keypad = devm_input_allocate_device(&client->dev);
+ if (!iqs7222->keypad)
+ return -ENOMEM;
+
+ iqs7222->keypad->name = client->name;
+ iqs7222->keypad->id.bustype = BUS_I2C;
+
+ /*
+ * The RDY pin behaves as an interrupt, but must also be polled ahead
+ * of unsolicited I2C communication. As such, it is first opened as a
+ * GPIO and then passed to gpiod_to_irq() to register the interrupt.
+ */
+ iqs7222->irq_gpio = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
+ if (IS_ERR(iqs7222->irq_gpio)) {
+ error = PTR_ERR(iqs7222->irq_gpio);
+ dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
+ error);
+ return error;
+ }
+
+ iqs7222->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(iqs7222->reset_gpio)) {
+ error = PTR_ERR(iqs7222->reset_gpio);
+ dev_err(&client->dev, "Failed to request reset GPIO: %d\n",
+ error);
+ return error;
+ }
+
+ error = iqs7222_hard_reset(iqs7222);
+ if (error)
+ return error;
+
+ error = iqs7222_dev_info(iqs7222);
+ if (error)
+ return error;
+
+ error = iqs7222_dev_init(iqs7222, READ);
+ if (error)
+ return error;
+
+ error = iqs7222_parse_all(iqs7222);
+ if (error)
+ return error;
+
+ error = iqs7222_dev_init(iqs7222, WRITE);
+ if (error)
+ return error;
+
+ error = iqs7222_report(iqs7222);
+ if (error)
+ return error;
+
+ error = input_register_device(iqs7222->keypad);
+ if (error) {
+ dev_err(&client->dev, "Failed to register device: %d\n", error);
+ return error;
+ }
+
+ irq = gpiod_to_irq(iqs7222->irq_gpio);
+ if (irq < 0)
+ return irq;
+
+ irq_flags = gpiod_is_active_low(iqs7222->irq_gpio) ? IRQF_TRIGGER_LOW
+ : IRQF_TRIGGER_HIGH;
+ irq_flags |= IRQF_ONESHOT;
+
+ error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7222_irq,
+ irq_flags, client->name, iqs7222);
+ if (error)
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+
+ return error;
+}
+
+static const struct of_device_id iqs7222_of_match[] = {
+ { .compatible = "azoteq,iqs7222a" },
+ { .compatible = "azoteq,iqs7222b" },
+ { .compatible = "azoteq,iqs7222c" },
+ { .compatible = "azoteq,iqs7222d" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, iqs7222_of_match);
+
+static struct i2c_driver iqs7222_i2c_driver = {
+ .driver = {
+ .name = "iqs7222",
+ .of_match_table = iqs7222_of_match,
+ },
+ .probe = iqs7222_probe,
+};
+module_i2c_driver(iqs7222_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS7222A/B/C/D Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
deleted file mode 100644
index 1fe149f3def2..000000000000
--- a/drivers/input/misc/ixp4xx-beeper.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Generic IXP4xx beeper driver
- *
- * Copyright (C) 2005 Tower Technologies
- *
- * based on nslu2-io.c
- * Copyright (C) 2004 Karen Spearel
- *
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- * Maintainers: http://www.nslu2-linux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <mach/hardware.h>
-
-MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
-MODULE_DESCRIPTION("ixp4xx beeper driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ixp4xx-beeper");
-
-static DEFINE_SPINLOCK(beep_lock);
-
-static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&beep_lock, flags);
-
- if (count) {
- gpio_direction_output(pin, 0);
- *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
- } else {
- gpio_direction_output(pin, 1);
- gpio_direction_input(pin);
- *IXP4XX_OSRT2 = 0;
- }
-
- spin_unlock_irqrestore(&beep_lock, flags);
-}
-
-static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
- unsigned int pin = (unsigned int) input_get_drvdata(dev);
- unsigned int count = 0;
-
- if (type != EV_SND)
- return -1;
-
- switch (code) {
- case SND_BELL:
- if (value)
- value = 1000;
- case SND_TONE:
- break;
- default:
- return -1;
- }
-
- if (value > 20 && value < 32767)
- count = (ixp4xx_timer_freq / (value * 4)) - 1;
-
- ixp4xx_spkr_control(pin, count);
-
- return 0;
-}
-
-static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id)
-{
- unsigned int pin = (unsigned int) dev_id;
-
- /* clear interrupt */
- *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND;
-
- /* flip the beeper output */
- gpio_set_value(pin, !gpio_get_value(pin));
-
- return IRQ_HANDLED;
-}
-
-static int ixp4xx_spkr_probe(struct platform_device *dev)
-{
- struct input_dev *input_dev;
- int err;
-
- input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
-
- input_set_drvdata(input_dev, (void *) dev->id);
-
- input_dev->name = "ixp4xx beeper",
- input_dev->phys = "ixp4xx/gpio";
- input_dev->id.bustype = BUS_HOST;
- input_dev->id.vendor = 0x001f;
- input_dev->id.product = 0x0001;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &dev->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_SND);
- input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
- input_dev->event = ixp4xx_spkr_event;
-
- err = gpio_request(dev->id, "ixp4-beeper");
- if (err)
- goto err_free_device;
-
- err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
- IRQF_NO_SUSPEND, "ixp4xx-beeper",
- (void *) dev->id);
- if (err)
- goto err_free_gpio;
-
- err = input_register_device(input_dev);
- if (err)
- goto err_free_irq;
-
- platform_set_drvdata(dev, input_dev);
-
- return 0;
-
- err_free_irq:
- free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id);
- err_free_gpio:
- gpio_free(dev->id);
- err_free_device:
- input_free_device(input_dev);
-
- return err;
-}
-
-static int ixp4xx_spkr_remove(struct platform_device *dev)
-{
- struct input_dev *input_dev = platform_get_drvdata(dev);
- unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
-
- input_unregister_device(input_dev);
-
- /* turn the speaker off */
- disable_irq(IRQ_IXP4XX_TIMER2);
- ixp4xx_spkr_control(pin, 0);
-
- free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id);
- gpio_free(dev->id);
-
- return 0;
-}
-
-static void ixp4xx_spkr_shutdown(struct platform_device *dev)
-{
- struct input_dev *input_dev = platform_get_drvdata(dev);
- unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
-
- /* turn off the speaker */
- disable_irq(IRQ_IXP4XX_TIMER2);
- ixp4xx_spkr_control(pin, 0);
-}
-
-static struct platform_driver ixp4xx_spkr_platform_driver = {
- .driver = {
- .name = "ixp4xx-beeper",
- },
- .probe = ixp4xx_spkr_probe,
- .remove = ixp4xx_spkr_remove,
- .shutdown = ixp4xx_spkr_shutdown,
-};
-module_platform_driver(ixp4xx_spkr_platform_driver);
-
diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c
index a8937ceac66a..bee4b1376491 100644
--- a/drivers/input/misc/keyspan_remote.c
+++ b/drivers/input/misc/keyspan_remote.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* keyspan_remote: USB driver for the Keyspan DMR
*
* Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
* This driver has been put together with the support of Innosys, Inc.
* and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
*/
@@ -339,7 +336,8 @@ static int keyspan_setup(struct usb_device* dev)
int retval = 0;
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
+ 0x11, 0x40, 0x5601, 0x0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
__func__, retval);
@@ -347,7 +345,8 @@ static int keyspan_setup(struct usb_device* dev)
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
+ 0x44, 0x40, 0x0, 0x0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
__func__, retval);
@@ -355,7 +354,8 @@ static int keyspan_setup(struct usb_device* dev)
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
+ 0x22, 0x40, 0x0, 0x0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
__func__, retval);
@@ -485,7 +485,7 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic
}
if (udev->manufacturer)
- strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
+ strscpy(remote->name, udev->manufacturer, sizeof(remote->name));
if (udev->product) {
if (udev->manufacturer)
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index efaffcc57e36..eb9788ea5215 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Kionix, Inc.
* Written by Chris Hudson <chudson@kionix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA
*/
#include <linux/delay.h>
@@ -24,7 +11,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input/kxtj9.h>
-#include <linux/input-polldev.h>
#define NAME "kxtj9"
#define G_MAX 8000
@@ -39,7 +25,7 @@
/* CONTROL REGISTER 1 BITS */
#define PC1_OFF 0x7F
#define PC1_ON (1 << 7)
-/* Data ready funtion enable bit: set during probe if using irq mode */
+/* Data ready function enable bit: set during probe if using irq mode */
#define DRDYE (1 << 5)
/* DATA CONTROL REGISTER BITS */
#define ODR12_5F 0
@@ -84,9 +70,6 @@ struct kxtj9_data {
struct i2c_client *client;
struct kxtj9_platform_data pdata;
struct input_dev *input_dev;
-#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
- struct input_polled_dev *poll_dev;
-#endif
unsigned int last_poll_interval;
u8 shift;
u8 ctrl_reg1;
@@ -295,50 +278,6 @@ static void kxtj9_input_close(struct input_dev *dev)
kxtj9_disable(tj9);
}
-static void kxtj9_init_input_device(struct kxtj9_data *tj9,
- struct input_dev *input_dev)
-{
- __set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
- input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
- input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
-
- input_dev->name = "kxtj9_accel";
- input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &tj9->client->dev;
-}
-
-static int kxtj9_setup_input_device(struct kxtj9_data *tj9)
-{
- struct input_dev *input_dev;
- int err;
-
- input_dev = input_allocate_device();
- if (!input_dev) {
- dev_err(&tj9->client->dev, "input device allocate failed\n");
- return -ENOMEM;
- }
-
- tj9->input_dev = input_dev;
-
- input_dev->open = kxtj9_input_open;
- input_dev->close = kxtj9_input_close;
- input_set_drvdata(input_dev, tj9);
-
- kxtj9_init_input_device(tj9, input_dev);
-
- err = input_register_device(tj9->input_dev);
- if (err) {
- dev_err(&tj9->client->dev,
- "unable to register input polled device %s: %d\n",
- tj9->input_dev->name, err);
- input_free_device(tj9->input_dev);
- return err;
- }
-
- return 0;
-}
-
/*
* When IRQ mode is selected, we need to provide an interface to allow the user
* to change the output data rate of the part. For consistency, we are using
@@ -375,9 +314,8 @@ static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
return error;
/* Lock the device to prevent races with open/close (and itself) */
- mutex_lock(&input_dev->mutex);
-
- disable_irq(client->irq);
+ guard(mutex)(&input_dev->mutex);
+ guard(disable_irq)(&client->irq);
/*
* Set current interval to the greater of the minimum interval or
@@ -387,29 +325,35 @@ static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
kxtj9_update_odr(tj9, tj9->last_poll_interval);
- enable_irq(client->irq);
- mutex_unlock(&input_dev->mutex);
-
return count;
}
static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
-static struct attribute *kxtj9_attributes[] = {
+static struct attribute *kxtj9_attrs[] = {
&dev_attr_poll.attr,
NULL
};
-static struct attribute_group kxtj9_attribute_group = {
- .attrs = kxtj9_attributes
-};
+static umode_t kxtj9_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ return client->irq ? attr->mode : 0;
+}
-#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
-static void kxtj9_poll(struct input_polled_dev *dev)
+static struct attribute_group kxtj9_group = {
+ .attrs = kxtj9_attrs,
+ .is_visible = kxtj9_attr_is_visible,
+};
+__ATTRIBUTE_GROUPS(kxtj9);
+
+static void kxtj9_poll(struct input_dev *input)
{
- struct kxtj9_data *tj9 = dev->private;
- unsigned int poll_interval = dev->poll_interval;
+ struct kxtj9_data *tj9 = input_get_drvdata(input);
+ unsigned int poll_interval = input_get_poll_interval(input);
kxtj9_report_acceleration_data(tj9);
@@ -419,72 +363,14 @@ static void kxtj9_poll(struct input_polled_dev *dev)
}
}
-static void kxtj9_polled_input_open(struct input_polled_dev *dev)
-{
- struct kxtj9_data *tj9 = dev->private;
-
- kxtj9_enable(tj9);
-}
-
-static void kxtj9_polled_input_close(struct input_polled_dev *dev)
-{
- struct kxtj9_data *tj9 = dev->private;
-
- kxtj9_disable(tj9);
-}
-
-static int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+static void kxtj9_platform_exit(void *data)
{
- int err;
- struct input_polled_dev *poll_dev;
- poll_dev = input_allocate_polled_device();
-
- if (!poll_dev) {
- dev_err(&tj9->client->dev,
- "Failed to allocate polled device\n");
- return -ENOMEM;
- }
-
- tj9->poll_dev = poll_dev;
- tj9->input_dev = poll_dev->input;
-
- poll_dev->private = tj9;
- poll_dev->poll = kxtj9_poll;
- poll_dev->open = kxtj9_polled_input_open;
- poll_dev->close = kxtj9_polled_input_close;
-
- kxtj9_init_input_device(tj9, poll_dev->input);
-
- err = input_register_polled_device(poll_dev);
- if (err) {
- dev_err(&tj9->client->dev,
- "Unable to register polled device, err=%d\n", err);
- input_free_polled_device(poll_dev);
- return err;
- }
-
- return 0;
-}
+ struct kxtj9_data *tj9 = data;
-static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
-{
- input_unregister_polled_device(tj9->poll_dev);
- input_free_polled_device(tj9->poll_dev);
-}
-
-#else
-
-static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
-{
- return -ENOSYS;
-}
-
-static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
-{
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
}
-#endif
-
static int kxtj9_verify(struct kxtj9_data *tj9)
{
int retval;
@@ -506,12 +392,12 @@ out:
return retval;
}
-static int kxtj9_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int kxtj9_probe(struct i2c_client *client)
{
const struct kxtj9_platform_data *pdata =
dev_get_platdata(&client->dev);
struct kxtj9_data *tj9;
+ struct input_dev *input_dev;
int err;
if (!i2c_check_functionality(client->adapter,
@@ -525,7 +411,7 @@ static int kxtj9_probe(struct i2c_client *client,
return -EINVAL;
}
- tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+ tj9 = devm_kzalloc(&client->dev, sizeof(*tj9), GFP_KERNEL);
if (!tj9) {
dev_err(&client->dev,
"failed to allocate memory for module data\n");
@@ -538,13 +424,17 @@ static int kxtj9_probe(struct i2c_client *client,
if (pdata->init) {
err = pdata->init();
if (err < 0)
- goto err_free_mem;
+ return err;
}
+ err = devm_add_action_or_reset(&client->dev, kxtj9_platform_exit, tj9);
+ if (err)
+ return err;
+
err = kxtj9_verify(tj9);
if (err < 0) {
dev_err(&client->dev, "device not recognized\n");
- goto err_pdata_exit;
+ return err;
}
i2c_set_clientdata(client, tj9);
@@ -552,115 +442,102 @@ static int kxtj9_probe(struct i2c_client *client,
tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
tj9->last_poll_interval = tj9->pdata.init_interval;
- if (client->irq) {
- /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
- tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
- tj9->ctrl_reg1 |= DRDYE;
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
- err = kxtj9_setup_input_device(tj9);
- if (err)
- goto err_pdata_exit;
+ input_set_drvdata(input_dev, tj9);
+ tj9->input_dev = input_dev;
- err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "kxtj9-irq", tj9);
- if (err) {
- dev_err(&client->dev, "request irq failed: %d\n", err);
- goto err_destroy_input;
- }
+ input_dev->name = "kxtj9_accel";
+ input_dev->id.bustype = BUS_I2C;
- err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
- if (err) {
- dev_err(&client->dev, "sysfs create failed: %d\n", err);
- goto err_free_irq;
- }
+ input_dev->open = kxtj9_input_open;
+ input_dev->close = kxtj9_input_close;
- } else {
- err = kxtj9_setup_polled_device(tj9);
+ input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+ if (client->irq <= 0) {
+ err = input_setup_polling(input_dev, kxtj9_poll);
if (err)
- goto err_pdata_exit;
+ return err;
}
- return 0;
-
-err_free_irq:
- free_irq(client->irq, tj9);
-err_destroy_input:
- input_unregister_device(tj9->input_dev);
-err_pdata_exit:
- if (tj9->pdata.exit)
- tj9->pdata.exit();
-err_free_mem:
- kfree(tj9);
- return err;
-}
-
-static int kxtj9_remove(struct i2c_client *client)
-{
- struct kxtj9_data *tj9 = i2c_get_clientdata(client);
-
- if (client->irq) {
- sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
- free_irq(client->irq, tj9);
- input_unregister_device(tj9->input_dev);
- } else {
- kxtj9_teardown_polled_device(tj9);
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev,
+ "unable to register input polled device %s: %d\n",
+ input_dev->name, err);
+ return err;
}
- if (tj9->pdata.exit)
- tj9->pdata.exit();
+ if (client->irq) {
+ /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+ tj9->ctrl_reg1 |= DRDYE;
- kfree(tj9);
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, kxtj9_isr,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ "kxtj9-irq", tj9);
+ if (err) {
+ dev_err(&client->dev, "request irq failed: %d\n", err);
+ return err;
+ }
+ }
return 0;
}
-static int __maybe_unused kxtj9_suspend(struct device *dev)
+static int kxtj9_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
struct input_dev *input_dev = tj9->input_dev;
- mutex_lock(&input_dev->mutex);
+ guard(mutex)(&input_dev->mutex);
- if (input_dev->users)
+ if (input_device_enabled(input_dev))
kxtj9_disable(tj9);
- mutex_unlock(&input_dev->mutex);
return 0;
}
-static int __maybe_unused kxtj9_resume(struct device *dev)
+static int kxtj9_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
struct input_dev *input_dev = tj9->input_dev;
- mutex_lock(&input_dev->mutex);
+ guard(mutex)(&input_dev->mutex);
- if (input_dev->users)
+ if (input_device_enabled(input_dev))
kxtj9_enable(tj9);
- mutex_unlock(&input_dev->mutex);
return 0;
}
-static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
static const struct i2c_device_id kxtj9_id[] = {
- { NAME, 0 },
- { },
+ { NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, kxtj9_id);
static struct i2c_driver kxtj9_driver = {
.driver = {
- .name = NAME,
- .pm = &kxtj9_pm_ops,
+ .name = NAME,
+ .dev_groups = kxtj9_groups,
+ .pm = pm_sleep_ptr(&kxtj9_pm_ops),
},
.probe = kxtj9_probe,
- .remove = kxtj9_remove,
.id_table = kxtj9_id,
};
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
index 312d63623038..0542334df624 100644
--- a/drivers/input/misc/m68kspkr.c
+++ b/drivers/input/misc/m68kspkr.c
@@ -1,17 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* m68k beeper driver for Linux
*
* Copyright (c) 2002 Richard Zidlicky
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
- *
*/
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation
- */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -80,15 +75,13 @@ static int m68kspkr_probe(struct platform_device *dev)
return 0;
}
-static int m68kspkr_remove(struct platform_device *dev)
+static void m68kspkr_remove(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
input_unregister_device(input_dev);
/* turn off the speaker */
m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
-
- return 0;
}
static void m68kspkr_shutdown(struct platform_device *dev)
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/max77650-onkey.c b/drivers/input/misc/max77650-onkey.c
new file mode 100644
index 000000000000..ee55f22dbca5
--- /dev/null
+++ b/drivers/input/misc/max77650-onkey.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// ONKEY driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MAX77650_ONKEY_MODE_MASK BIT(3)
+#define MAX77650_ONKEY_MODE_PUSH 0x00
+#define MAX77650_ONKEY_MODE_SLIDE BIT(3)
+
+struct max77650_onkey {
+ struct input_dev *input;
+ unsigned int code;
+};
+
+static irqreturn_t max77650_onkey_falling(int irq, void *data)
+{
+ struct max77650_onkey *onkey = data;
+
+ input_report_key(onkey->input, onkey->code, 0);
+ input_sync(onkey->input);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max77650_onkey_rising(int irq, void *data)
+{
+ struct max77650_onkey *onkey = data;
+
+ input_report_key(onkey->input, onkey->code, 1);
+ input_sync(onkey->input);
+
+ return IRQ_HANDLED;
+}
+
+static int max77650_onkey_probe(struct platform_device *pdev)
+{
+ int irq_r, irq_f, error, mode;
+ struct max77650_onkey *onkey;
+ struct device *dev, *parent;
+ struct regmap *map;
+ unsigned int type;
+
+ dev = &pdev->dev;
+ parent = dev->parent;
+
+ map = dev_get_regmap(parent, NULL);
+ if (!map)
+ return -ENODEV;
+
+ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ error = device_property_read_u32(dev, "linux,code", &onkey->code);
+ if (error)
+ onkey->code = KEY_POWER;
+
+ if (device_property_read_bool(dev, "maxim,onkey-slide")) {
+ mode = MAX77650_ONKEY_MODE_SLIDE;
+ type = EV_SW;
+ } else {
+ mode = MAX77650_ONKEY_MODE_PUSH;
+ type = EV_KEY;
+ }
+
+ error = regmap_update_bits(map, MAX77650_REG_CNFG_GLBL,
+ MAX77650_ONKEY_MODE_MASK, mode);
+ if (error)
+ return error;
+
+ irq_f = platform_get_irq_byname(pdev, "nEN_F");
+ if (irq_f < 0)
+ return irq_f;
+
+ irq_r = platform_get_irq_byname(pdev, "nEN_R");
+ if (irq_r < 0)
+ return irq_r;
+
+ onkey->input = devm_input_allocate_device(dev);
+ if (!onkey->input)
+ return -ENOMEM;
+
+ onkey->input->name = "max77650_onkey";
+ onkey->input->phys = "max77650_onkey/input0";
+ onkey->input->id.bustype = BUS_I2C;
+ input_set_capability(onkey->input, type, onkey->code);
+
+ error = devm_request_any_context_irq(dev, irq_f, max77650_onkey_falling,
+ IRQF_ONESHOT, "onkey-down", onkey);
+ if (error < 0)
+ return error;
+
+ error = devm_request_any_context_irq(dev, irq_r, max77650_onkey_rising,
+ IRQF_ONESHOT, "onkey-up", onkey);
+ if (error < 0)
+ return error;
+
+ return input_register_device(onkey->input);
+}
+
+static const struct of_device_id max77650_onkey_of_match[] = {
+ { .compatible = "maxim,max77650-onkey" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max77650_onkey_of_match);
+
+static struct platform_driver max77650_onkey_driver = {
+ .driver = {
+ .name = "max77650-onkey",
+ .of_match_table = max77650_onkey_of_match,
+ },
+ .probe = max77650_onkey_probe,
+};
+module_platform_driver(max77650_onkey_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:max77650-onkey");
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c
index 46b0f48fbf49..5d45680d74a4 100644
--- a/drivers/input/misc/max77693-haptic.c
+++ b/drivers/input/misc/max77693-haptic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MAXIM MAX77693/MAX77843 Haptic device driver
*
@@ -6,11 +7,6 @@
* Krzysztof Kozlowski <krzk@kernel.org>
*
* This program is not provided / owned by Maxim Integrated Products.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/err.h>
@@ -22,11 +18,13 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/workqueue.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77693-private.h>
+#include <linux/mfd/max77705-private.h>
#include <linux/mfd/max77843-private.h>
#define MAX_MAGNITUDE_SHIFT 16
@@ -70,15 +68,16 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
{
- struct pwm_args pargs;
- int delta;
+ struct pwm_state state;
int error;
- pwm_get_args(haptic->pwm_dev, &pargs);
- delta = (pargs.period + haptic->pwm_duty) / 2;
- error = pwm_config(haptic->pwm_dev, delta, pargs.period);
+ pwm_init_state(haptic->pwm_dev, &state);
+ state.duty_cycle = (state.period + haptic->pwm_duty) / 2;
+
+ error = pwm_apply_might_sleep(haptic->pwm_dev, &state);
if (error) {
- dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
+ dev_err(haptic->dev,
+ "failed to set pwm duty cycle: %d\n", error);
return error;
}
@@ -98,7 +97,7 @@ static int max77843_haptic_bias(struct max77693_haptic *haptic, bool on)
on << MAINCTRL1_BIASEN_SHIFT);
if (error) {
dev_err(haptic->dev, "failed to %s bias: %d\n",
- on ? "enable" : "disable", error);
+ str_enable_disable(on), error);
return error;
}
@@ -119,6 +118,13 @@ static int max77693_haptic_configure(struct max77693_haptic *haptic,
MAX77693_HAPTIC_PWM_DIVISOR_128);
config_reg = MAX77693_HAPTIC_REG_CONFIG2;
break;
+ case TYPE_MAX77705:
+ value = ((haptic->type << MAX77693_CONFIG2_MODE) |
+ (enable << MAX77693_CONFIG2_MEN) |
+ (haptic->mode << MAX77693_CONFIG2_HTYP) |
+ MAX77693_HAPTIC_PWM_DIVISOR_128);
+ config_reg = MAX77705_PMIC_REG_MCONFIG;
+ break;
case TYPE_MAX77843:
value = (haptic->type << MCONFIG_MODE_SHIFT) |
(enable << MCONFIG_MEN_SHIFT) |
@@ -161,12 +167,17 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
static void max77693_haptic_enable(struct max77693_haptic *haptic)
{
+ struct pwm_state state;
int error;
if (haptic->enabled)
return;
- error = pwm_enable(haptic->pwm_dev);
+ pwm_init_state(haptic->pwm_dev, &state);
+ state.duty_cycle = (state.period + haptic->pwm_duty) / 2;
+ state.enabled = true;
+
+ error = pwm_apply_might_sleep(haptic->pwm_dev, &state);
if (error) {
dev_err(haptic->dev,
"failed to enable haptic pwm device: %d\n", error);
@@ -219,18 +230,13 @@ static void max77693_haptic_play_work(struct work_struct *work)
{
struct max77693_haptic *haptic =
container_of(work, struct max77693_haptic, work);
- int error;
-
- error = max77693_haptic_set_duty_cycle(haptic);
- if (error) {
- dev_err(haptic->dev, "failed to set duty cycle: %d\n", error);
- return;
- }
- if (haptic->magnitude)
- max77693_haptic_enable(haptic);
- else
+ if (!haptic->magnitude)
max77693_haptic_disable(haptic);
+ else if (haptic->enabled)
+ max77693_haptic_set_duty_cycle(haptic);
+ else
+ max77693_haptic_enable(haptic);
}
static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
@@ -311,11 +317,12 @@ static int max77693_haptic_probe(struct platform_device *pdev)
haptic->suspend_state = false;
/* Variant-specific init */
- haptic->dev_type = platform_get_device_id(pdev)->driver_data;
+ haptic->dev_type = max77693->type;
switch (haptic->dev_type) {
case TYPE_MAX77693:
haptic->regmap_haptic = max77693->regmap_haptic;
break;
+ case TYPE_MAX77705:
case TYPE_MAX77843:
haptic->regmap_haptic = max77693->regmap;
break;
@@ -334,12 +341,6 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return PTR_ERR(haptic->pwm_dev);
}
- /*
- * FIXME: pwm_apply_args() should be removed when switching to the
- * atomic PWM API.
- */
- pwm_apply_args(haptic->pwm_dev);
-
haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
if (IS_ERR(haptic->motor_reg)) {
dev_err(&pdev->dev, "failed to get regulator\n");
@@ -379,7 +380,7 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused max77693_haptic_suspend(struct device *dev)
+static int max77693_haptic_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct max77693_haptic *haptic = platform_get_drvdata(pdev);
@@ -392,7 +393,7 @@ static int __maybe_unused max77693_haptic_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused max77693_haptic_resume(struct device *dev)
+static int max77693_haptic_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct max77693_haptic *haptic = platform_get_drvdata(pdev);
@@ -405,20 +406,31 @@ static int __maybe_unused max77693_haptic_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops,
- max77693_haptic_suspend, max77693_haptic_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops,
+ max77693_haptic_suspend,
+ max77693_haptic_resume);
static const struct platform_device_id max77693_haptic_id[] = {
- { "max77693-haptic", TYPE_MAX77693 },
- { "max77843-haptic", TYPE_MAX77843 },
+ { "max77693-haptic", },
+ { "max77705-haptic", },
+ { "max77843-haptic", },
{},
};
MODULE_DEVICE_TABLE(platform, max77693_haptic_id);
+static const struct of_device_id of_max77693_haptic_dt_match[] = {
+ { .compatible = "maxim,max77693-haptic", },
+ { .compatible = "maxim,max77705-haptic", },
+ { .compatible = "maxim,max77843-haptic", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, of_max77693_haptic_dt_match);
+
static struct platform_driver max77693_haptic_driver = {
.driver = {
.name = "max77693-haptic",
- .pm = &max77693_haptic_pm_ops,
+ .pm = pm_sleep_ptr(&max77693_haptic_pm_ops),
+ .of_match_table = of_max77693_haptic_dt_match,
},
.probe = max77693_haptic_probe,
.id_table = max77693_haptic_id,
@@ -427,6 +439,5 @@ module_platform_driver(max77693_haptic_driver);
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
-MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
-MODULE_ALIAS("platform:max77693-haptic");
+MODULE_DESCRIPTION("MAXIM 77693/77705/77843 Haptic driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
index 7c49b8d23894..62619e4fed20 100644
--- a/drivers/input/misc/max8925_onkey.c
+++ b/drivers/input/misc/max8925_onkey.c
@@ -1,4 +1,4 @@
-/**
+/*
* MAX8925 ONKEY driver
*
* Copyright (C) 2009 Marvell International Ltd.
@@ -71,16 +71,12 @@ static int max8925_onkey_probe(struct platform_device *pdev)
int irq[2], error;
irq[0] = platform_get_irq(pdev, 0);
- if (irq[0] < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
+ if (irq[0] < 0)
return -EINVAL;
- }
irq[1] = platform_get_irq(pdev, 1);
- if (irq[1] < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
+ if (irq[1] < 0)
return -EINVAL;
- }
info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_onkey_info),
GFP_KERNEL);
@@ -133,7 +129,7 @@ static int max8925_onkey_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused max8925_onkey_suspend(struct device *dev)
+static int max8925_onkey_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct max8925_onkey_info *info = platform_get_drvdata(pdev);
@@ -147,7 +143,7 @@ static int __maybe_unused max8925_onkey_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused max8925_onkey_resume(struct device *dev)
+static int max8925_onkey_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct max8925_onkey_info *info = platform_get_drvdata(pdev);
@@ -161,12 +157,13 @@ static int __maybe_unused max8925_onkey_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops,
+ max8925_onkey_suspend, max8925_onkey_resume);
static struct platform_driver max8925_onkey_driver = {
.driver = {
.name = "max8925-onkey",
- .pm = &max8925_onkey_pm_ops,
+ .pm = pm_sleep_ptr(&max8925_onkey_pm_ops),
},
.probe = max8925_onkey_probe,
};
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index 99bc762881d5..d5e051a25a74 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MAX8997-haptic controller driver
*
@@ -5,21 +6,6 @@
* Donggeun Kim <dg77.kim@samsung.com>
*
* This program is not provided / owned by Maxim Integrated Products.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/module.h>
@@ -67,45 +53,30 @@ struct max8997_haptic {
unsigned int pattern_signal_period;
};
-static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
+static void max8997_haptic_set_internal_duty_cycle(struct max8997_haptic *chip)
{
- int ret = 0;
+ u8 duty_index = DIV_ROUND_UP(chip->level * 64, 100);
- if (chip->mode == MAX8997_EXTERNAL_MODE) {
- unsigned int duty = chip->pwm_period * chip->level / 100;
- ret = pwm_config(chip->pwm, duty, chip->pwm_period);
- } else {
- int i;
- u8 duty_index = 0;
-
- for (i = 0; i <= 64; i++) {
- if (chip->level <= i * 100 / 64) {
- duty_index = i;
- break;
- }
- }
- switch (chip->internal_mode_pattern) {
- case 0:
- max8997_write_reg(chip->client,
- MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
- break;
- case 1:
- max8997_write_reg(chip->client,
- MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
- break;
- case 2:
- max8997_write_reg(chip->client,
- MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
- break;
- case 3:
- max8997_write_reg(chip->client,
- MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
- break;
- default:
- break;
- }
+ switch (chip->internal_mode_pattern) {
+ case 0:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
+ break;
+ case 1:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
+ break;
+ case 2:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
+ break;
+ case 3:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
+ break;
+ default:
+ break;
}
- return ret;
}
static void max8997_haptic_configure(struct max8997_haptic *chip)
@@ -172,39 +143,49 @@ static void max8997_haptic_enable(struct max8997_haptic *chip)
{
int error;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
- error = max8997_haptic_set_duty_cycle(chip);
- if (error) {
- dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error);
- goto out;
- }
+ if (chip->mode != MAX8997_EXTERNAL_MODE)
+ max8997_haptic_set_internal_duty_cycle(chip);
if (!chip->enabled) {
error = regulator_enable(chip->regulator);
if (error) {
dev_err(chip->dev, "Failed to enable regulator\n");
- goto out;
+ return;
}
max8997_haptic_configure(chip);
- if (chip->mode == MAX8997_EXTERNAL_MODE) {
- error = pwm_enable(chip->pwm);
- if (error) {
- dev_err(chip->dev, "Failed to enable PWM\n");
- regulator_disable(chip->regulator);
- goto out;
- }
+ }
+
+ /*
+ * It would be more straight forward to configure the external PWM
+ * earlier i.e. when the internal duty_cycle is setup in internal mode.
+ * But historically this is done only after the regulator was enabled
+ * and max8997_haptic_configure() set the enable bit in
+ * MAX8997_HAPTIC_REG_CONF2. So better keep it this way.
+ */
+ if (chip->mode == MAX8997_EXTERNAL_MODE) {
+ struct pwm_state state;
+
+ pwm_init_state(chip->pwm, &state);
+ state.period = chip->pwm_period;
+ state.duty_cycle = chip->pwm_period * chip->level / 100;
+ state.enabled = true;
+
+ error = pwm_apply_might_sleep(chip->pwm, &state);
+ if (error) {
+ dev_err(chip->dev, "Failed to enable PWM\n");
+ regulator_disable(chip->regulator);
+ return;
}
- chip->enabled = true;
}
-out:
- mutex_unlock(&chip->mutex);
+ chip->enabled = true;
}
static void max8997_haptic_disable(struct max8997_haptic *chip)
{
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (chip->enabled) {
chip->enabled = false;
@@ -213,8 +194,6 @@ static void max8997_haptic_disable(struct max8997_haptic *chip)
pwm_disable(chip->pwm);
regulator_disable(chip->regulator);
}
-
- mutex_unlock(&chip->mutex);
}
static void max8997_haptic_play_effect_work(struct work_struct *work)
@@ -268,7 +247,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
return -EINVAL;
}
- chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL);
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
input_dev = input_allocate_device();
if (!chip || !input_dev) {
dev_err(&pdev->dev, "unable to allocate memory\n");
@@ -297,8 +276,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
break;
case MAX8997_EXTERNAL_MODE:
- chip->pwm = pwm_request(haptic_pdata->pwm_channel_id,
- "max8997-haptic");
+ chip->pwm = pwm_get(&pdev->dev, NULL);
if (IS_ERR(chip->pwm)) {
error = PTR_ERR(chip->pwm);
dev_err(&pdev->dev,
@@ -307,11 +285,6 @@ static int max8997_haptic_probe(struct platform_device *pdev)
goto err_free_mem;
}
- /*
- * FIXME: pwm_apply_args() should be removed when switching to
- * the atomic PWM API.
- */
- pwm_apply_args(chip->pwm);
break;
default:
@@ -363,7 +336,7 @@ err_put_regulator:
regulator_put(chip->regulator);
err_free_pwm:
if (chip->mode == MAX8997_EXTERNAL_MODE)
- pwm_free(chip->pwm);
+ pwm_put(chip->pwm);
err_free_mem:
input_free_device(input_dev);
kfree(chip);
@@ -371,7 +344,7 @@ err_free_mem:
return error;
}
-static int max8997_haptic_remove(struct platform_device *pdev)
+static void max8997_haptic_remove(struct platform_device *pdev)
{
struct max8997_haptic *chip = platform_get_drvdata(pdev);
@@ -379,14 +352,12 @@ static int max8997_haptic_remove(struct platform_device *pdev)
regulator_put(chip->regulator);
if (chip->mode == MAX8997_EXTERNAL_MODE)
- pwm_free(chip->pwm);
+ pwm_put(chip->pwm);
kfree(chip);
-
- return 0;
}
-static int __maybe_unused max8997_haptic_suspend(struct device *dev)
+static int max8997_haptic_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct max8997_haptic *chip = platform_get_drvdata(pdev);
@@ -396,7 +367,8 @@ static int __maybe_unused max8997_haptic_suspend(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL);
+static DEFINE_SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops,
+ max8997_haptic_suspend, NULL);
static const struct platform_device_id max8997_haptic_id[] = {
{ "max8997-haptic", 0 },
@@ -407,7 +379,7 @@ MODULE_DEVICE_TABLE(platform, max8997_haptic_id);
static struct platform_driver max8997_haptic_driver = {
.driver = {
.name = "max8997-haptic",
- .pm = &max8997_haptic_pm_ops,
+ .pm = pm_sleep_ptr(&max8997_haptic_pm_ops),
},
.probe = max8997_haptic_probe,
.remove = max8997_haptic_remove,
diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c
index afdf8ef25ee8..b83d762ae2e9 100644
--- a/drivers/input/misc/mc13783-pwrbutton.c
+++ b/drivers/input/misc/mc13783-pwrbutton.c
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2011 Philippe Rétornaz
*
* Based on twl4030-pwrbutton driver by:
@@ -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) {
@@ -229,7 +228,7 @@ free_input_dev:
return err;
}
-static int mc13783_pwrbutton_remove(struct platform_device *pdev)
+static void mc13783_pwrbutton_remove(struct platform_device *pdev)
{
struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
const struct mc13xxx_buttons_platform_data *pdata;
@@ -249,8 +248,6 @@ static int mc13783_pwrbutton_remove(struct platform_device *pdev)
input_unregister_device(priv->pwr);
kfree(priv);
-
- return 0;
}
static struct platform_driver mc13783_pwrbutton_driver = {
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
index b60cdea73826..0c661140fb88 100644
--- a/drivers/input/misc/mma8450.c
+++ b/drivers/input/misc/mma8450.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Freescale's 3-Axis Accelerometer MMA8450
*
* Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -23,8 +10,8 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/input-polldev.h>
-#include <linux/of_device.h>
+#include <linux/input.h>
+#include <linux/mod_devicetable.h>
#define MMA8450_DRV_NAME "mma8450"
@@ -51,16 +38,11 @@
#define MMA8450_CTRL_REG1 0x38
#define MMA8450_CTRL_REG2 0x39
+#define MMA8450_ID 0xc6
+#define MMA8450_WHO_AM_I 0x0f
-/* mma8450 status */
-struct mma8450 {
- struct i2c_client *client;
- struct input_polled_dev *idev;
-};
-
-static int mma8450_read(struct mma8450 *m, unsigned off)
+static int mma8450_read(struct i2c_client *c, unsigned int off)
{
- struct i2c_client *c = m->client;
int ret;
ret = i2c_smbus_read_byte_data(c, off);
@@ -72,9 +54,8 @@ static int mma8450_read(struct mma8450 *m, unsigned off)
return ret;
}
-static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+static int mma8450_write(struct i2c_client *c, unsigned int off, u8 v)
{
- struct i2c_client *c = m->client;
int error;
error = i2c_smbus_write_byte_data(c, off, v);
@@ -88,10 +69,9 @@ static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
return 0;
}
-static int mma8450_read_block(struct mma8450 *m, unsigned off,
+static int mma8450_read_block(struct i2c_client *c, unsigned int off,
u8 *buf, size_t size)
{
- struct i2c_client *c = m->client;
int err;
err = i2c_smbus_read_i2c_block_data(c, off, size, buf);
@@ -105,21 +85,21 @@ static int mma8450_read_block(struct mma8450 *m, unsigned off,
return 0;
}
-static void mma8450_poll(struct input_polled_dev *dev)
+static void mma8450_poll(struct input_dev *input)
{
- struct mma8450 *m = dev->private;
+ struct i2c_client *c = input_get_drvdata(input);
int x, y, z;
int ret;
u8 buf[6];
- ret = mma8450_read(m, MMA8450_STATUS);
+ ret = mma8450_read(c, MMA8450_STATUS);
if (ret < 0)
return;
if (!(ret & MMA8450_STATUS_ZXYDR))
return;
- ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf));
+ ret = mma8450_read_block(c, MMA8450_OUT_X_LSB, buf, sizeof(buf));
if (ret < 0)
return;
@@ -127,81 +107,92 @@ static void mma8450_poll(struct input_polled_dev *dev)
y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf);
z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf);
- input_report_abs(dev->input, ABS_X, x);
- input_report_abs(dev->input, ABS_Y, y);
- input_report_abs(dev->input, ABS_Z, z);
- input_sync(dev->input);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_Z, z);
+ input_sync(input);
}
/* Initialize the MMA8450 chip */
-static void mma8450_open(struct input_polled_dev *dev)
+static int mma8450_open(struct input_dev *input)
{
- struct mma8450 *m = dev->private;
+ struct i2c_client *c = input_get_drvdata(input);
int err;
/* enable all events from X/Y/Z, no FIFO */
- err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+ err = mma8450_write(c, MMA8450_XYZ_DATA_CFG, 0x07);
if (err)
- return;
+ return err;
/*
* Sleep mode poll rate - 50Hz
* System output data rate - 400Hz
* Full scale selection - Active, +/- 2G
*/
- err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
- if (err < 0)
- return;
+ err = mma8450_write(c, MMA8450_CTRL_REG1, 0x01);
+ if (err)
+ return err;
msleep(MODE_CHANGE_DELAY_MS);
+ return 0;
}
-static void mma8450_close(struct input_polled_dev *dev)
+static void mma8450_close(struct input_dev *input)
{
- struct mma8450 *m = dev->private;
+ struct i2c_client *c = input_get_drvdata(input);
- mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
- mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+ mma8450_write(c, MMA8450_CTRL_REG1, 0x00);
+ mma8450_write(c, MMA8450_CTRL_REG2, 0x01);
}
/*
* I2C init/probing/exit functions
*/
-static int mma8450_probe(struct i2c_client *c,
- const struct i2c_device_id *id)
+static int mma8450_probe(struct i2c_client *c)
{
- struct input_polled_dev *idev;
- struct mma8450 *m;
- int err;
-
- m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL);
- if (!m)
+ struct i2c_adapter *adapter = c->adapter;
+ struct input_dev *input;
+ int err, client_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return dev_err_probe(&c->dev, -EINVAL,
+ "I2C adapter doesn't support SMBUS BYTE");
+
+ client_id = i2c_smbus_read_byte_data(c, MMA8450_WHO_AM_I);
+ if (client_id != MMA8450_ID)
+ return dev_err_probe(&c->dev, -EINVAL,
+ "unexpected chip ID 0x%x (vs 0x%x)\n",
+ client_id, MMA8450_ID);
+
+ input = devm_input_allocate_device(&c->dev);
+ if (!input)
return -ENOMEM;
- idev = devm_input_allocate_polled_device(&c->dev);
- if (!idev)
- return -ENOMEM;
+ input_set_drvdata(input, c);
+
+ input->name = MMA8450_DRV_NAME;
+ input->id.bustype = BUS_I2C;
- m->client = c;
- m->idev = idev;
+ input->open = mma8450_open;
+ input->close = mma8450_close;
- idev->private = m;
- idev->input->name = MMA8450_DRV_NAME;
- idev->input->id.bustype = BUS_I2C;
- idev->poll = mma8450_poll;
- idev->poll_interval = POLL_INTERVAL;
- idev->poll_interval_max = POLL_INTERVAL_MAX;
- idev->open = mma8450_open;
- idev->close = mma8450_close;
+ input_set_abs_params(input, ABS_X, -2048, 2047, 32, 32);
+ input_set_abs_params(input, ABS_Y, -2048, 2047, 32, 32);
+ input_set_abs_params(input, ABS_Z, -2048, 2047, 32, 32);
+
+ err = input_setup_polling(input, mma8450_poll);
+ if (err) {
+ dev_err(&c->dev, "failed to set up polling\n");
+ return err;
+ }
- __set_bit(EV_ABS, idev->input->evbit);
- input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
- input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
- input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+ input_set_poll_interval(input, POLL_INTERVAL);
+ input_set_max_poll_interval(input, POLL_INTERVAL_MAX);
- err = input_register_polled_device(idev);
+ err = input_register_device(input);
if (err) {
- dev_err(&c->dev, "failed to register polled input device\n");
+ dev_err(&c->dev, "failed to register input device\n");
return err;
}
@@ -209,8 +200,8 @@ static int mma8450_probe(struct i2c_client *c,
}
static const struct i2c_device_id mma8450_id[] = {
- { MMA8450_DRV_NAME, 0 },
- { },
+ { MMA8450_DRV_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mma8450_id);
diff --git a/drivers/input/misc/nxp-bbnsm-pwrkey.c b/drivers/input/misc/nxp-bbnsm-pwrkey.c
new file mode 100644
index 000000000000..0c7b8f8ef4a5
--- /dev/null
+++ b/drivers/input/misc/nxp-bbnsm-pwrkey.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2022 NXP.
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+
+#define BBNSM_CTRL 0x8
+#define BBNSM_INT_EN 0x10
+#define BBNSM_EVENTS 0x14
+#define BBNSM_PAD_CTRL 0x24
+
+#define BBNSM_BTN_PRESSED BIT(7)
+#define BBNSM_PWR_ON BIT(6)
+#define BBNSM_BTN_OFF BIT(5)
+#define BBNSM_EMG_OFF BIT(4)
+#define BBNSM_PWRKEY_EVENTS (BBNSM_PWR_ON | BBNSM_BTN_OFF | BBNSM_EMG_OFF)
+#define BBNSM_DP_EN BIT(24)
+
+#define DEBOUNCE_TIME 30
+#define REPEAT_INTERVAL 60
+
+struct bbnsm_pwrkey {
+ struct regmap *regmap;
+ int irq;
+ int keycode;
+ int keystate; /* 1:pressed */
+ bool suspended;
+ struct timer_list check_timer;
+ struct input_dev *input;
+};
+
+static void bbnsm_pwrkey_check_for_events(struct timer_list *t)
+{
+ struct bbnsm_pwrkey *bbnsm = timer_container_of(bbnsm, t, check_timer);
+ struct input_dev *input = bbnsm->input;
+ u32 state;
+
+ regmap_read(bbnsm->regmap, BBNSM_EVENTS, &state);
+
+ state = state & BBNSM_BTN_PRESSED ? 1 : 0;
+
+ /* only report new event if status changed */
+ if (state ^ bbnsm->keystate) {
+ bbnsm->keystate = state;
+ input_event(input, EV_KEY, bbnsm->keycode, state);
+ input_sync(input);
+ pm_relax(bbnsm->input->dev.parent);
+ }
+
+ /* repeat check if pressed long */
+ if (state)
+ mod_timer(&bbnsm->check_timer,
+ jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
+}
+
+static irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
+ struct input_dev *input = bbnsm->input;
+ u32 event;
+
+ regmap_read(bbnsm->regmap, BBNSM_EVENTS, &event);
+ if (!(event & BBNSM_BTN_OFF))
+ return IRQ_NONE;
+
+ pm_wakeup_event(bbnsm->input->dev.parent, 0);
+
+ /*
+ * Directly report key event after resume to make sure key press
+ * event is never missed.
+ */
+ if (bbnsm->suspended) {
+ bbnsm->keystate = 1;
+ input_event(input, EV_KEY, bbnsm->keycode, 1);
+ input_sync(input);
+ /* Fire at most once per suspend/resume cycle */
+ bbnsm->suspended = false;
+ }
+
+ mod_timer(&bbnsm->check_timer,
+ jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
+
+ /* clear PWR OFF */
+ regmap_write(bbnsm->regmap, BBNSM_EVENTS, BBNSM_BTN_OFF);
+
+ return IRQ_HANDLED;
+}
+
+static void bbnsm_pwrkey_act(void *pdata)
+{
+ struct bbnsm_pwrkey *bbnsm = pdata;
+
+ timer_shutdown_sync(&bbnsm->check_timer);
+}
+
+static int bbnsm_pwrkey_probe(struct platform_device *pdev)
+{
+ struct bbnsm_pwrkey *bbnsm;
+ struct input_dev *input;
+ struct device_node *np = pdev->dev.of_node;
+ int error;
+
+ bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
+ if (!bbnsm)
+ return -ENOMEM;
+
+ bbnsm->regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(bbnsm->regmap)) {
+ dev_err(&pdev->dev, "bbnsm pwerkey get regmap failed\n");
+ return PTR_ERR(bbnsm->regmap);
+ }
+
+ if (device_property_read_u32(&pdev->dev, "linux,code",
+ &bbnsm->keycode)) {
+ bbnsm->keycode = KEY_POWER;
+ dev_warn(&pdev->dev, "key code is not specified, using default KEY_POWER\n");
+ }
+
+ bbnsm->irq = platform_get_irq(pdev, 0);
+ if (bbnsm->irq < 0)
+ return -EINVAL;
+
+ /* config the BBNSM power related register */
+ regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, BBNSM_DP_EN, BBNSM_DP_EN);
+
+ /* clear the unexpected interrupt before driver ready */
+ regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, BBNSM_PWRKEY_EVENTS,
+ BBNSM_PWRKEY_EVENTS);
+
+ timer_setup(&bbnsm->check_timer, bbnsm_pwrkey_check_for_events, 0);
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ input->name = pdev->name;
+ input->phys = "bbnsm-pwrkey/input0";
+ input->id.bustype = BUS_HOST;
+
+ input_set_capability(input, EV_KEY, bbnsm->keycode);
+
+ /* input customer action to cancel release timer */
+ error = devm_add_action(&pdev->dev, bbnsm_pwrkey_act, bbnsm);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register remove action\n");
+ return error;
+ }
+
+ bbnsm->input = input;
+ platform_set_drvdata(pdev, bbnsm);
+
+ error = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_pwrkey_interrupt,
+ IRQF_SHARED, pdev->name, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "interrupt not available.\n");
+ return error;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+ error = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
+ if (error)
+ dev_warn(&pdev->dev, "irq wake enable failed.\n");
+
+ return 0;
+}
+
+static void bbnsm_pwrkey_remove(struct platform_device *pdev)
+{
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
+}
+
+static int __maybe_unused bbnsm_pwrkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
+
+ bbnsm->suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused bbnsm_pwrkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
+
+ bbnsm->suspended = false;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(bbnsm_pwrkey_pm_ops, bbnsm_pwrkey_suspend,
+ bbnsm_pwrkey_resume);
+
+static const struct of_device_id bbnsm_pwrkey_ids[] = {
+ { .compatible = "nxp,imx93-bbnsm-pwrkey" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, bbnsm_pwrkey_ids);
+
+static struct platform_driver bbnsm_pwrkey_driver = {
+ .driver = {
+ .name = "bbnsm_pwrkey",
+ .pm = &bbnsm_pwrkey_pm_ops,
+ .of_match_table = bbnsm_pwrkey_ids,
+ },
+ .probe = bbnsm_pwrkey_probe,
+ .remove = bbnsm_pwrkey_remove,
+
+};
+module_platform_driver(bbnsm_pwrkey_driver);
+
+MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
+MODULE_DESCRIPTION("NXP bbnsm power key Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
index 1e1baed63929..39fc451c56e9 100644
--- a/drivers/input/misc/palmas-pwrbutton.c
+++ b/drivers/input/misc/palmas-pwrbutton.c
@@ -1,20 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Texas Instruments' Palmas Power Button Input Driver
*
* Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
* Girish S Ghongdemath
* Nishanth Menon
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+#include <linux/bitfield.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@@ -115,8 +108,8 @@ static void palmas_pwron_params_ofinit(struct device *dev,
struct device_node *np;
u32 val;
int i, error;
- u8 lpk_times[] = { 6, 8, 10, 12 };
- int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
+ static const u8 lpk_times[] = { 6, 8, 10, 12 };
+ static const int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
memset(config, 0, sizeof(*config));
@@ -192,8 +185,8 @@ static int palmas_pwron_probe(struct platform_device *pdev)
* Setup default hardware shutdown option (long key press)
* and debounce.
*/
- val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK);
- val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK);
+ val = FIELD_PREP(PALMAS_LPK_TIME_MASK, config.long_press_time_val) |
+ FIELD_PREP(PALMAS_PWRON_DEBOUNCE_MASK, config.pwron_debounce_val);
error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
PALMAS_LONG_PRESS_KEY,
PALMAS_LPK_TIME_MASK |
@@ -210,6 +203,11 @@ static int palmas_pwron_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
pwron->irq = platform_get_irq(pdev, 0);
+ if (pwron->irq < 0) {
+ error = pwron->irq;
+ goto err_free_input;
+ }
+
error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
IRQF_TRIGGER_HIGH |
IRQF_TRIGGER_LOW |
@@ -247,7 +245,7 @@ err_free_mem:
*
* Return: 0
*/
-static int palmas_pwron_remove(struct platform_device *pdev)
+static void palmas_pwron_remove(struct platform_device *pdev)
{
struct palmas_pwron *pwron = platform_get_drvdata(pdev);
@@ -256,8 +254,6 @@ static int palmas_pwron_remove(struct platform_device *pdev)
input_unregister_device(pwron->input_dev);
kfree(pwron);
-
- return 0;
}
/**
@@ -268,7 +264,7 @@ static int palmas_pwron_remove(struct platform_device *pdev)
*
* Return: 0
*/
-static int __maybe_unused palmas_pwron_suspend(struct device *dev)
+static int palmas_pwron_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct palmas_pwron *pwron = platform_get_drvdata(pdev);
@@ -289,7 +285,7 @@ static int __maybe_unused palmas_pwron_suspend(struct device *dev)
*
* Return: 0
*/
-static int __maybe_unused palmas_pwron_resume(struct device *dev)
+static int palmas_pwron_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct palmas_pwron *pwron = platform_get_drvdata(pdev);
@@ -300,8 +296,8 @@ static int __maybe_unused palmas_pwron_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
- palmas_pwron_suspend, palmas_pwron_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
+ palmas_pwron_suspend, palmas_pwron_resume);
#ifdef CONFIG_OF
static const struct of_device_id of_palmas_pwr_match[] = {
@@ -318,7 +314,7 @@ static struct platform_driver palmas_pwron_driver = {
.driver = {
.name = "palmas_pwrbutton",
.of_match_table = of_match_ptr(of_palmas_pwr_match),
- .pm = &palmas_pwron_pm,
+ .pm = pm_sleep_ptr(&palmas_pwron_pm),
},
};
module_platform_driver(palmas_pwron_driver);
diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c
index 3b81daf67726..fe43fd72ba7b 100644
--- a/drivers/input/misc/pcap_keys.c
+++ b/drivers/input/misc/pcap_keys.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Input driver for PCAP events:
* * Power key
* * Headphone button
*
* Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -53,7 +49,7 @@ static int pcap_keys_probe(struct platform_device *pdev)
struct pcap_keys *pcap_keys;
struct input_dev *input_dev;
- pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL);
+ pcap_keys = kmalloc(sizeof(*pcap_keys), GFP_KERNEL);
if (!pcap_keys)
return err;
@@ -103,7 +99,7 @@ fail:
return err;
}
-static int pcap_keys_remove(struct platform_device *pdev)
+static void pcap_keys_remove(struct platform_device *pdev)
{
struct pcap_keys *pcap_keys = platform_get_drvdata(pdev);
@@ -112,8 +108,6 @@ static int pcap_keys_remove(struct platform_device *pdev)
input_unregister_device(pcap_keys->input);
kfree(pcap_keys);
-
- return 0;
}
static struct platform_driver pcap_keys_device_driver = {
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
deleted file mode 100644
index db92f4f3c99b..000000000000
--- a/drivers/input/misc/pcf50633-input.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* NXP PCF50633 Input Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/pcf50633/core.h>
-
-#define PCF50633_OOCSTAT_ONKEY 0x01
-#define PCF50633_REG_OOCSTAT 0x12
-#define PCF50633_REG_OOCMODE 0x10
-
-struct pcf50633_input {
- struct pcf50633 *pcf;
- struct input_dev *input_dev;
-};
-
-static void
-pcf50633_input_irq(int irq, void *data)
-{
- struct pcf50633_input *input;
- int onkey_released;
-
- input = data;
-
- /* We report only one event depending on the key press status */
- onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
- & PCF50633_OOCSTAT_ONKEY;
-
- if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
- input_report_key(input->input_dev, KEY_POWER, 1);
- else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
- input_report_key(input->input_dev, KEY_POWER, 0);
-
- input_sync(input->input_dev);
-}
-
-static int pcf50633_input_probe(struct platform_device *pdev)
-{
- struct pcf50633_input *input;
- struct input_dev *input_dev;
- int ret;
-
-
- input = kzalloc(sizeof(*input), GFP_KERNEL);
- if (!input)
- return -ENOMEM;
-
- input_dev = input_allocate_device();
- if (!input_dev) {
- kfree(input);
- return -ENOMEM;
- }
-
- platform_set_drvdata(pdev, input);
- input->pcf = dev_to_pcf50633(pdev->dev.parent);
- input->input_dev = input_dev;
-
- input_dev->name = "PCF50633 PMU events";
- input_dev->id.bustype = BUS_I2C;
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
- set_bit(KEY_POWER, input_dev->keybit);
-
- ret = input_register_device(input_dev);
- if (ret) {
- input_free_device(input_dev);
- kfree(input);
- return ret;
- }
- pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
- pcf50633_input_irq, input);
- pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
- pcf50633_input_irq, input);
-
- return 0;
-}
-
-static int pcf50633_input_remove(struct platform_device *pdev)
-{
- struct pcf50633_input *input = platform_get_drvdata(pdev);
-
- pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
- pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
-
- input_unregister_device(input->input_dev);
- kfree(input);
-
- return 0;
-}
-
-static struct platform_driver pcf50633_input_driver = {
- .driver = {
- .name = "pcf50633-input",
- },
- .probe = pcf50633_input_probe,
- .remove = pcf50633_input_remove,
-};
-module_platform_driver(pcf50633_input_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 input driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-input");
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
index 4abdf1efb3e0..3632cb206e34 100644
--- a/drivers/input/misc/pcf8574_keypad.c
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
*
* Copyright 2005-2008 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -81,7 +80,7 @@ static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int pcf8574_kp_probe(struct i2c_client *client)
{
int i, ret;
struct input_dev *idev;
@@ -158,7 +157,7 @@ static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_i
return ret;
}
-static int pcf8574_kp_remove(struct i2c_client *client)
+static void pcf8574_kp_remove(struct i2c_client *client)
{
struct kp_data *lp = i2c_get_clientdata(client);
@@ -166,11 +165,8 @@ static int pcf8574_kp_remove(struct i2c_client *client)
input_unregister_device(lp->idev);
kfree(lp);
-
- return 0;
}
-#ifdef CONFIG_PM
static int pcf8574_kp_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -189,18 +185,11 @@ static int pcf8574_kp_suspend(struct device *dev)
return 0;
}
-static const struct dev_pm_ops pcf8574_kp_pm_ops = {
- .suspend = pcf8574_kp_suspend,
- .resume = pcf8574_kp_resume,
-};
-
-#else
-# define pcf8574_kp_resume NULL
-# define pcf8574_kp_suspend NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(pcf8574_kp_pm_ops,
+ pcf8574_kp_suspend, pcf8574_kp_resume);
static const struct i2c_device_id pcf8574_kp_id[] = {
- { DRV_NAME, 0 },
+ { DRV_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
@@ -208,9 +197,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
static struct i2c_driver pcf8574_kp_driver = {
.driver = {
.name = DRV_NAME,
-#ifdef CONFIG_PM
- .pm = &pcf8574_kp_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&pcf8574_kp_pm_ops),
},
.probe = pcf8574_kp_probe,
.remove = pcf8574_kp_remove,
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
index 56ddba21de84..0467808402f2 100644
--- a/drivers/input/misc/pcspkr.c
+++ b/drivers/input/misc/pcspkr.c
@@ -1,16 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
- *
*/
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation
- */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -38,6 +33,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (value)
value = 1000;
+ break;
case SND_TONE:
break;
default:
@@ -99,15 +95,13 @@ static int pcspkr_probe(struct platform_device *dev)
return 0;
}
-static int pcspkr_remove(struct platform_device *dev)
+static void pcspkr_remove(struct platform_device *dev)
{
struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
input_unregister_device(pcspkr_dev);
/* turn off the speaker */
pcspkr_event(NULL, EV_SND, SND_BELL, 0);
-
- return 0;
}
static int pcspkr_suspend(struct device *dev)
diff --git a/drivers/input/misc/pf1550-onkey.c b/drivers/input/misc/pf1550-onkey.c
new file mode 100644
index 000000000000..9be6377151cb
--- /dev/null
+++ b/drivers/input/misc/pf1550-onkey.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the PF1550 ONKEY
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
+ */
+
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/pf1550.h>
+#include <linux/platform_device.h>
+
+#define PF1550_ONKEY_IRQ_NR 6
+
+struct onkey_drv_data {
+ struct device *dev;
+ const struct pf1550_ddata *pf1550;
+ bool wakeup;
+ struct input_dev *input;
+};
+
+static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)
+{
+ struct onkey_drv_data *onkey = data;
+ struct platform_device *pdev = to_platform_device(onkey->dev);
+ int i, state, irq_type = -1;
+
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++)
+ if (irq == platform_get_irq(pdev, i))
+ irq_type = i;
+
+ switch (irq_type) {
+ case PF1550_ONKEY_IRQ_PUSHI:
+ state = 0;
+ break;
+ case PF1550_ONKEY_IRQ_1SI:
+ case PF1550_ONKEY_IRQ_2SI:
+ case PF1550_ONKEY_IRQ_3SI:
+ case PF1550_ONKEY_IRQ_4SI:
+ case PF1550_ONKEY_IRQ_8SI:
+ state = 1;
+ break;
+ default:
+ dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",
+ irq_type);
+ return IRQ_HANDLED;
+ }
+
+ input_event(onkey->input, EV_KEY, KEY_POWER, state);
+ input_sync(onkey->input);
+
+ return IRQ_HANDLED;
+}
+
+static int pf1550_onkey_probe(struct platform_device *pdev)
+{
+ struct onkey_drv_data *onkey;
+ struct input_dev *input;
+ bool key_power = false;
+ int i, irq, error;
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ onkey->dev = &pdev->dev;
+
+ onkey->pf1550 = dev_get_drvdata(pdev->dev.parent);
+ if (!onkey->pf1550->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to get regmap\n");
+
+ onkey->wakeup = device_property_read_bool(pdev->dev.parent,
+ "wakeup-source");
+
+ if (device_property_read_bool(pdev->dev.parent,
+ "nxp,disable-key-power")) {
+ error = regmap_clear_bits(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_PWRCTRL1,
+ PF1550_ONKEY_RST_EN);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed: disable turn system off");
+ } else {
+ key_power = true;
+ }
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "failed to allocate the input device\n");
+
+ input->name = pdev->name;
+ input->phys = "pf1550-onkey/input0";
+ input->id.bustype = BUS_HOST;
+
+ if (key_power)
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
+ onkey->input = input;
+ platform_set_drvdata(pdev, onkey);
+
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ pf1550_onkey_irq_handler,
+ IRQF_NO_SUSPEND,
+ "pf1550-onkey", onkey);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed: irq request (IRQ: %d)\n",
+ i);
+ }
+
+ error = input_register_device(input);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed to register input device\n");
+
+ device_init_wakeup(&pdev->dev, onkey->wakeup);
+
+ return 0;
+}
+
+static int pf1550_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+ int i, irq;
+
+ if (!device_may_wakeup(&pdev->dev))
+ regmap_write(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_ONKEY_INT_MASK0,
+ ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
+ ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);
+ else
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ enable_irq_wake(irq);
+ }
+
+ return 0;
+}
+
+static int pf1550_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+ int i, irq;
+
+ if (!device_may_wakeup(&pdev->dev))
+ regmap_write(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_ONKEY_INT_MASK0,
+ ~((u8)(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI |
+ ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI |
+ ONKEY_IRQ_8SI)));
+ else
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ disable_irq_wake(irq);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,
+ pf1550_onkey_resume);
+
+static const struct platform_device_id pf1550_onkey_id[] = {
+ { "pf1550-onkey", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, pf1550_onkey_id);
+
+static struct platform_driver pf1550_onkey_driver = {
+ .driver = {
+ .name = "pf1550-onkey",
+ .pm = pm_sleep_ptr(&pf1550_onkey_pm_ops),
+ },
+ .probe = pf1550_onkey_probe,
+ .id_table = pf1550_onkey_id,
+};
+module_platform_driver(pf1550_onkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("PF1550 onkey Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
index 48153e0ca19a..53249d2c081f 100644
--- a/drivers/input/misc/pm8941-pwrkey.c
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2011, 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2014, Sony Mobile Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/delay.h>
@@ -17,24 +9,38 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#define PON_REV2 0x01
+#define PON_SUBTYPE 0x05
+
+#define PON_SUBTYPE_PRIMARY 0x01
+#define PON_SUBTYPE_SECONDARY 0x02
+#define PON_SUBTYPE_1REG 0x03
+#define PON_SUBTYPE_GEN2_PRIMARY 0x04
+#define PON_SUBTYPE_GEN2_SECONDARY 0x05
+#define PON_SUBTYPE_GEN3_PBS 0x08
+#define PON_SUBTYPE_GEN3_HLOS 0x09
+
#define PON_RT_STS 0x10
#define PON_KPDPWR_N_SET BIT(0)
#define PON_RESIN_N_SET BIT(1)
+#define PON_GEN3_RESIN_N_SET BIT(6)
+#define PON_GEN3_KPDPWR_N_SET BIT(7)
#define PON_PS_HOLD_RST_CTL 0x5a
#define PON_PS_HOLD_RST_CTL2 0x5b
#define PON_PS_HOLD_ENABLE BIT(7)
#define PON_PS_HOLD_TYPE_MASK 0x0f
+#define PON_PS_HOLD_TYPE_WARM_RESET 1
#define PON_PS_HOLD_TYPE_SHUTDOWN 4
#define PON_PS_HOLD_TYPE_HARD_RESET 7
@@ -43,24 +49,38 @@
#define PON_RESIN_PULL_UP BIT(0)
#define PON_DBC_CTL 0x71
-#define PON_DBC_DELAY_MASK 0x7
+#define PON_DBC_DELAY_MASK_GEN1 0x7
+#define PON_DBC_DELAY_MASK_GEN2 0xf
+#define PON_DBC_SHIFT_GEN1 6
+#define PON_DBC_SHIFT_GEN2 14
struct pm8941_data {
- unsigned int pull_up_bit;
- unsigned int status_bit;
+ unsigned int pull_up_bit;
+ unsigned int status_bit;
+ bool supports_ps_hold_poff_config;
+ bool supports_debounce_config;
+ bool has_pon_pbs;
+ bool wakeup_source_default;
+ const char *name;
+ const char *phys;
};
struct pm8941_pwrkey {
struct device *dev;
int irq;
u32 baseaddr;
+ u32 pon_pbs_baseaddr;
struct regmap *regmap;
struct input_dev *input;
unsigned int revision;
+ unsigned int subtype;
struct notifier_block reboot_notifier;
u32 code;
+ u32 sw_debounce_time_us;
+ ktime_t sw_debounce_end_time;
+ bool last_status;
const struct pm8941_data *data;
};
@@ -101,7 +121,10 @@ static int pm8941_reboot_notify(struct notifier_block *nb,
break;
case SYS_RESTART:
default:
- reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
+ if (reboot_mode == REBOOT_WARM)
+ reset_type = PON_PS_HOLD_TYPE_WARM_RESET;
+ else
+ reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
break;
}
@@ -127,21 +150,77 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
{
struct pm8941_pwrkey *pwrkey = _data;
unsigned int sts;
- int error;
+ int err;
+
+ if (pwrkey->sw_debounce_time_us) {
+ if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
+ dev_dbg(pwrkey->dev,
+ "ignoring key event received before debounce end %lld us\n",
+ ktime_to_us(pwrkey->sw_debounce_end_time));
+ return IRQ_HANDLED;
+ }
+ }
- error = regmap_read(pwrkey->regmap,
- pwrkey->baseaddr + PON_RT_STS, &sts);
- if (error)
+ err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
+ if (err)
return IRQ_HANDLED;
- input_report_key(pwrkey->input, pwrkey->code,
- sts & pwrkey->data->status_bit);
+ sts &= pwrkey->data->status_bit;
+
+ if (pwrkey->sw_debounce_time_us && !sts)
+ pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
+ pwrkey->sw_debounce_time_us);
+
+ /*
+ * Simulate a press event in case a release event occurred without a
+ * corresponding press event.
+ */
+ if (!pwrkey->last_status && !sts) {
+ input_report_key(pwrkey->input, pwrkey->code, 1);
+ input_sync(pwrkey->input);
+ }
+ pwrkey->last_status = sts;
+
+ input_report_key(pwrkey->input, pwrkey->code, sts);
input_sync(pwrkey->input);
return IRQ_HANDLED;
}
-static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
+static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
+{
+ unsigned int val, addr, mask;
+ int error;
+
+ if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
+ dev_err(pwrkey->dev,
+ "PON_PBS address missing, can't read HW debounce time\n");
+ return 0;
+ }
+
+ if (pwrkey->pon_pbs_baseaddr)
+ addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
+ else
+ addr = pwrkey->baseaddr + PON_DBC_CTL;
+ error = regmap_read(pwrkey->regmap, addr, &val);
+ if (error)
+ return error;
+
+ if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
+ mask = 0xf;
+ else
+ mask = 0x7;
+
+ pwrkey->sw_debounce_time_us =
+ 2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
+
+ dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
+ pwrkey->sw_debounce_time_us);
+
+ return 0;
+}
+
+static int pm8941_pwrkey_suspend(struct device *dev)
{
struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -151,7 +230,7 @@ static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
+static int pm8941_pwrkey_resume(struct device *dev)
{
struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -161,15 +240,17 @@ static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
- pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
+ pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
static int pm8941_pwrkey_probe(struct platform_device *pdev)
{
struct pm8941_pwrkey *pwrkey;
- bool pull_up;
+ bool pull_up, wakeup;
struct device *parent;
- u32 req_delay;
+ struct device_node *regmap_node;
+ const __be32 *addr;
+ u32 req_delay, mask, delay_shift;
int error;
if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
@@ -190,8 +271,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
pwrkey->data = of_device_get_match_data(&pdev->dev);
parent = pdev->dev.parent;
+ regmap_node = pdev->dev.of_node;
pwrkey->regmap = dev_get_regmap(parent, NULL);
if (!pwrkey->regmap) {
+ regmap_node = parent->of_node;
/*
* We failed to get regmap for parent. Let's see if we are
* a child of pon node and read regmap and reg from its
@@ -202,26 +285,37 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to locate regmap\n");
return -ENODEV;
}
+ }
- error = of_property_read_u32(parent->of_node,
- "reg", &pwrkey->baseaddr);
- } else {
- error = of_property_read_u32(pdev->dev.of_node, "reg",
- &pwrkey->baseaddr);
+ addr = of_get_address(regmap_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(&pdev->dev, "reg property missing\n");
+ return -EINVAL;
+ }
+ pwrkey->baseaddr = be32_to_cpup(addr);
+
+ if (pwrkey->data->has_pon_pbs) {
+ /* PON_PBS base address is optional */
+ addr = of_get_address(regmap_node, 1, NULL, NULL);
+ if (addr)
+ pwrkey->pon_pbs_baseaddr = be32_to_cpup(addr);
}
- if (error)
- return error;
pwrkey->irq = platform_get_irq(pdev, 0);
- if (pwrkey->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
+ if (pwrkey->irq < 0)
return pwrkey->irq;
- }
error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
&pwrkey->revision);
if (error) {
- dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+ dev_err(&pdev->dev, "failed to read revision: %d\n", error);
+ return error;
+ }
+
+ error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
+ &pwrkey->subtype);
+ if (error) {
+ dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
return error;
}
@@ -241,34 +335,52 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
input_set_capability(pwrkey->input, EV_KEY, pwrkey->code);
- pwrkey->input->name = "pm8941_pwrkey";
- pwrkey->input->phys = "pm8941_pwrkey/input0";
+ pwrkey->input->name = pwrkey->data->name;
+ pwrkey->input->phys = pwrkey->data->phys;
- req_delay = (req_delay << 6) / USEC_PER_SEC;
- req_delay = ilog2(req_delay);
+ if (pwrkey->data->supports_debounce_config) {
+ if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
+ mask = PON_DBC_DELAY_MASK_GEN2;
+ delay_shift = PON_DBC_SHIFT_GEN2;
+ } else {
+ mask = PON_DBC_DELAY_MASK_GEN1;
+ delay_shift = PON_DBC_SHIFT_GEN1;
+ }
- error = regmap_update_bits(pwrkey->regmap,
- pwrkey->baseaddr + PON_DBC_CTL,
- PON_DBC_DELAY_MASK,
- req_delay);
- if (error) {
- dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
- return error;
+ req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
+ req_delay = ilog2(req_delay);
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + PON_DBC_CTL,
+ mask,
+ req_delay);
+ if (error) {
+ dev_err(&pdev->dev, "failed to set debounce: %d\n",
+ error);
+ return error;
+ }
}
- error = regmap_update_bits(pwrkey->regmap,
- pwrkey->baseaddr + PON_PULL_CTL,
- pwrkey->data->pull_up_bit,
- pull_up ? pwrkey->data->pull_up_bit : 0);
- if (error) {
- dev_err(&pdev->dev, "failed to set pull: %d\n", error);
+ error = pm8941_pwrkey_sw_debounce_init(pwrkey);
+ if (error)
return error;
+
+ if (pwrkey->data->pull_up_bit) {
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + PON_PULL_CTL,
+ pwrkey->data->pull_up_bit,
+ pull_up ? pwrkey->data->pull_up_bit :
+ 0);
+ if (error) {
+ dev_err(&pdev->dev, "failed to set pull: %d\n", error);
+ return error;
+ }
}
error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
NULL, pm8941_pwrkey_irq,
IRQF_ONESHOT,
- "pm8941_pwrkey", pwrkey);
+ pwrkey->data->name, pwrkey);
if (error) {
dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
return error;
@@ -281,42 +393,80 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
return error;
}
- pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
- error = register_reboot_notifier(&pwrkey->reboot_notifier);
- if (error) {
- dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
- error);
- return error;
+ if (pwrkey->data->supports_ps_hold_poff_config) {
+ pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify;
+ error = register_reboot_notifier(&pwrkey->reboot_notifier);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
+ error);
+ return error;
+ }
}
+ 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;
}
-static int pm8941_pwrkey_remove(struct platform_device *pdev)
+static void pm8941_pwrkey_remove(struct platform_device *pdev)
{
struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
- unregister_reboot_notifier(&pwrkey->reboot_notifier);
-
- return 0;
+ if (pwrkey->data->supports_ps_hold_poff_config)
+ unregister_reboot_notifier(&pwrkey->reboot_notifier);
}
static const struct pm8941_data pwrkey_data = {
.pull_up_bit = PON_KPDPWR_PULL_UP,
.status_bit = PON_KPDPWR_N_SET,
+ .name = "pm8941_pwrkey",
+ .phys = "pm8941_pwrkey/input0",
+ .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 = {
.pull_up_bit = PON_RESIN_PULL_UP,
.status_bit = PON_RESIN_N_SET,
+ .name = "pm8941_resin",
+ .phys = "pm8941_resin/input0",
+ .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 = {
+ .status_bit = PON_GEN3_KPDPWR_N_SET,
+ .name = "pmic_pwrkey",
+ .phys = "pmic_pwrkey/input0",
+ .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 = {
+ .status_bit = PON_GEN3_RESIN_N_SET,
+ .name = "pmic_resin",
+ .phys = "pmic_resin/input0",
+ .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[] = {
{ .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
{ .compatible = "qcom,pm8941-resin", .data = &resin_data },
+ { .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data },
+ { .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data },
{ }
};
MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
@@ -326,7 +476,7 @@ static struct platform_driver pm8941_pwrkey_driver = {
.remove = pm8941_pwrkey_remove,
.driver = {
.name = "pm8941-pwrkey",
- .pm = &pm8941_pwr_key_pm_ops,
+ .pm = pm_sleep_ptr(&pm8941_pwr_key_pm_ops),
.of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
},
};
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
index 7dd1c1fbe42a..381b06473279 100644
--- a/drivers/input/misc/pm8xxx-vibrator.c
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -1,13 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/errno.h>
@@ -15,41 +7,61 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#define VIB_MAX_LEVEL_mV (3100)
-#define VIB_MIN_LEVEL_mV (1200)
-#define VIB_MAX_LEVELS (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+#define VIB_MAX_LEVEL_mV(vib) (vib->drv2_addr ? 3544 : 3100)
+#define VIB_MIN_LEVEL_mV(vib) (vib->drv2_addr ? 1504 : 1200)
+#define VIB_PER_STEP_mV(vib) (vib->drv2_addr ? 8 : 100)
+#define VIB_MAX_LEVELS(vib) \
+ (VIB_MAX_LEVEL_mV(vib) - VIB_MIN_LEVEL_mV(vib) + VIB_PER_STEP_mV(vib))
#define MAX_FF_SPEED 0xff
struct pm8xxx_regs {
- unsigned int enable_addr;
+ unsigned int enable_offset;
unsigned int enable_mask;
- unsigned int drv_addr;
+ unsigned int drv_offset;
unsigned int drv_mask;
unsigned int drv_shift;
+ unsigned int drv2_offset;
+ unsigned int drv2_mask;
+ unsigned int drv2_shift;
unsigned int drv_en_manual_mask;
+ bool drv_in_step;
};
static const struct pm8xxx_regs pm8058_regs = {
- .drv_addr = 0x4A,
- .drv_mask = 0xf8,
+ .drv_offset = 0,
+ .drv_mask = GENMASK(7, 3),
.drv_shift = 3,
.drv_en_manual_mask = 0xfc,
+ .drv_in_step = true,
};
static struct pm8xxx_regs pm8916_regs = {
- .enable_addr = 0xc046,
+ .enable_offset = 0x46,
+ .enable_mask = BIT(7),
+ .drv_offset = 0x41,
+ .drv_mask = GENMASK(4, 0),
+ .drv_shift = 0,
+ .drv_en_manual_mask = 0,
+ .drv_in_step = true,
+};
+
+static struct pm8xxx_regs pmi632_regs = {
+ .enable_offset = 0x46,
.enable_mask = BIT(7),
- .drv_addr = 0xc041,
- .drv_mask = 0x1F,
+ .drv_offset = 0x40,
+ .drv_mask = GENMASK(7, 0),
.drv_shift = 0,
+ .drv2_offset = 0x41,
+ .drv2_mask = GENMASK(3, 0),
+ .drv2_shift = 8,
.drv_en_manual_mask = 0,
+ .drv_in_step = false,
};
/**
@@ -58,6 +70,9 @@ static struct pm8xxx_regs pm8916_regs = {
* @work: work structure to set the vibration parameters
* @regmap: regmap for register read/write
* @regs: registers' info
+ * @enable_addr: vibrator enable register
+ * @drv_addr: vibrator drive strength register
+ * @drv2_addr: vibrator drive strength upper byte register
* @speed: speed of vibration set from userland
* @active: state of vibrator
* @level: level of vibration to set in the chip
@@ -68,6 +83,9 @@ struct pm8xxx_vib {
struct work_struct work;
struct regmap *regmap;
const struct pm8xxx_regs *regs;
+ unsigned int enable_addr;
+ unsigned int drv_addr;
+ unsigned int drv2_addr;
int speed;
int level;
bool active;
@@ -85,20 +103,31 @@ static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
unsigned int val = vib->reg_vib_drv;
const struct pm8xxx_regs *regs = vib->regs;
+ if (regs->drv_in_step)
+ vib->level /= VIB_PER_STEP_mV(vib);
+
if (on)
val |= (vib->level << regs->drv_shift) & regs->drv_mask;
else
val &= ~regs->drv_mask;
- rc = regmap_write(vib->regmap, regs->drv_addr, val);
+ rc = regmap_write(vib->regmap, vib->drv_addr, val);
if (rc < 0)
return rc;
vib->reg_vib_drv = val;
+ if (regs->drv2_mask) {
+ val = vib->level << regs->drv2_shift;
+ rc = regmap_write_bits(vib->regmap, vib->drv2_addr,
+ regs->drv2_mask, on ? val : 0);
+ if (rc < 0)
+ return rc;
+ }
+
if (regs->enable_mask)
- rc = regmap_update_bits(vib->regmap, regs->enable_addr,
- on ? regs->enable_mask : 0, val);
+ rc = regmap_update_bits(vib->regmap, vib->enable_addr,
+ regs->enable_mask, on ? regs->enable_mask : 0);
return rc;
}
@@ -110,26 +139,24 @@ static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
static void pm8xxx_work_handler(struct work_struct *work)
{
struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
- const struct pm8xxx_regs *regs = vib->regs;
- int rc;
unsigned int val;
+ int rc;
- rc = regmap_read(vib->regmap, regs->drv_addr, &val);
+ rc = regmap_read(vib->regmap, vib->drv_addr, &val);
if (rc < 0)
return;
/*
- * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+ * pmic vibrator supports voltage ranges from MIN_LEVEL to MAX_LEVEL, so
* scale the level to fit into these ranges.
*/
if (vib->speed) {
vib->active = true;
- vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) +
- VIB_MIN_LEVEL_mV;
- vib->level /= 100;
+ vib->level = VIB_MIN_LEVEL_mV(vib);
+ vib->level += mult_frac(VIB_MAX_LEVELS(vib), vib->speed, MAX_FF_SPEED);
} else {
vib->active = false;
- vib->level = VIB_MIN_LEVEL_mV / 100;
+ vib->level = VIB_MIN_LEVEL_mV(vib);
}
pm8xxx_vib_set(vib, vib->active);
@@ -177,7 +204,7 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
struct pm8xxx_vib *vib;
struct input_dev *input_dev;
int error;
- unsigned int val;
+ unsigned int val, reg_base = 0;
const struct pm8xxx_regs *regs;
vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
@@ -195,15 +222,22 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
INIT_WORK(&vib->work, pm8xxx_work_handler);
vib->vib_input_dev = input_dev;
+ error = fwnode_property_read_u32(pdev->dev.fwnode, "reg", &reg_base);
+ if (error < 0)
+ return dev_err_probe(&pdev->dev, error, "Failed to read reg address\n");
+
regs = of_device_get_match_data(&pdev->dev);
+ vib->enable_addr = reg_base + regs->enable_offset;
+ vib->drv_addr = reg_base + regs->drv_offset;
+ vib->drv2_addr = reg_base + regs->drv2_offset;
/* operate in manual mode */
- error = regmap_read(vib->regmap, regs->drv_addr, &val);
+ error = regmap_read(vib->regmap, vib->drv_addr, &val);
if (error < 0)
return error;
val &= regs->drv_en_manual_mask;
- error = regmap_write(vib->regmap, regs->drv_addr, val);
+ error = regmap_write(vib->regmap, vib->drv_addr, val);
if (error < 0)
return error;
@@ -234,7 +268,7 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused pm8xxx_vib_suspend(struct device *dev)
+static int pm8xxx_vib_suspend(struct device *dev)
{
struct pm8xxx_vib *vib = dev_get_drvdata(dev);
@@ -244,12 +278,13 @@ static int __maybe_unused pm8xxx_vib_suspend(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+static DEFINE_SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
static const struct of_device_id pm8xxx_vib_id_table[] = {
{ .compatible = "qcom,pm8058-vib", .data = &pm8058_regs },
{ .compatible = "qcom,pm8921-vib", .data = &pm8058_regs },
{ .compatible = "qcom,pm8916-vib", .data = &pm8916_regs },
+ { .compatible = "qcom,pmi632-vib", .data = &pmi632_regs },
{ }
};
MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table);
@@ -258,7 +293,7 @@ static struct platform_driver pm8xxx_vib_driver = {
.probe = pm8xxx_vib_probe,
.driver = {
.name = "pm8xxx-vib",
- .pm = &pm8xxx_vib_pm_ops,
+ .pm = pm_sleep_ptr(&pm8xxx_vib_pm_ops),
.of_match_table = pm8xxx_vib_id_table,
},
};
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 73323b0c72c1..c406a1cca5c4 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -1,13 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -20,7 +12,6 @@
#include <linux/regmap.h>
#include <linux/log2.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#define PON_CNTL_1 0x1C
#define PON_CNTL_PULL_UP BIT(7)
@@ -108,7 +99,7 @@ static irqreturn_t pwrkey_release_irq(int irq, void *_pwr)
return IRQ_HANDLED;
}
-static int __maybe_unused pmic8xxx_pwrkey_suspend(struct device *dev)
+static int pmic8xxx_pwrkey_suspend(struct device *dev)
{
struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -118,7 +109,7 @@ static int __maybe_unused pmic8xxx_pwrkey_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused pmic8xxx_pwrkey_resume(struct device *dev)
+static int pmic8xxx_pwrkey_resume(struct device *dev)
{
struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -128,7 +119,7 @@ static int __maybe_unused pmic8xxx_pwrkey_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
static void pmic8xxx_pwrkey_shutdown(struct platform_device *pdev)
@@ -450,7 +441,7 @@ static struct platform_driver pmic8xxx_pwrkey_driver = {
.shutdown = pmic8xxx_pwrkey_shutdown,
.driver = {
.name = "pm8xxx-pwrkey",
- .pm = &pm8xxx_pwr_key_pm_ops,
+ .pm = pm_sleep_ptr(&pm8xxx_pwr_key_pm_ops),
.of_match_table = pm8xxx_pwr_key_id_table,
},
};
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
index e8de3aaf9f63..ecb92ee5ebbc 100644
--- a/drivers/input/misc/powermate.c
+++ b/drivers/input/misc/powermate.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
*
@@ -193,22 +194,18 @@ static void powermate_sync_state(struct powermate_device *pm)
static void powermate_config_complete(struct urb *urb)
{
struct powermate_device *pm = urb->context;
- unsigned long flags;
if (urb->status)
printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
- spin_lock_irqsave(&pm->lock, flags);
+ guard(spinlock_irqsave)(&pm->lock);
powermate_sync_state(pm);
- spin_unlock_irqrestore(&pm->lock, flags);
}
/* Set the LED up as described and begin the sync with the hardware if required */
static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
int pulse_table, int pulse_asleep, int pulse_awake)
{
- unsigned long flags;
-
if (pulse_speed < 0)
pulse_speed = 0;
if (pulse_table < 0)
@@ -221,8 +218,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne
pulse_asleep = !!pulse_asleep;
pulse_awake = !!pulse_awake;
-
- spin_lock_irqsave(&pm->lock, flags);
+ guard(spinlock_irqsave)(&pm->lock);
/* mark state updates which are required */
if (static_brightness != pm->static_brightness) {
@@ -244,8 +240,6 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne
}
powermate_sync_state(pm);
-
- spin_unlock_irqrestore(&pm->lock, flags);
}
/* Callback from the Input layer when an event arrives from userspace to configure the LED */
@@ -319,7 +313,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
0, interface->desc.bInterfaceNumber, NULL, 0,
USB_CTRL_SET_TIMEOUT);
- pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
+ pm = kzalloc(sizeof(*pm), GFP_KERNEL);
input_dev = input_allocate_device();
if (!pm || !input_dev)
goto fail1;
@@ -373,7 +367,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
- maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = usb_maxpacket(udev, pipe);
if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
@@ -424,6 +418,7 @@ static void powermate_disconnect(struct usb_interface *intf)
pm->requires_update = 0;
usb_kill_urb(pm->irq);
input_unregister_device(pm->input);
+ usb_kill_urb(pm->config);
usb_free_urb(pm->irq);
usb_free_urb(pm->config);
powermate_free_buffers(interface_to_usbdev(intf), pm);
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index edca0d737750..0e19e97d98ec 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
* PWM beeper driver
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/input.h>
@@ -48,7 +39,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period)
state.period = period;
pwm_set_relative_duty_cycle(&state, 50, 100);
- error = pwm_apply_state(beeper->pwm, &state);
+ error = pwm_apply_might_sleep(beeper->pwm, &state);
if (error)
return error;
@@ -141,18 +132,13 @@ static int pwm_beeper_probe(struct platform_device *pdev)
return -ENOMEM;
beeper->pwm = devm_pwm_get(dev, NULL);
- if (IS_ERR(beeper->pwm)) {
- error = PTR_ERR(beeper->pwm);
- if (error != -EPROBE_DEFER)
- dev_err(dev, "Failed to request PWM device: %d\n",
- error);
- return error;
- }
+ if (IS_ERR(beeper->pwm))
+ return dev_err_probe(dev, PTR_ERR(beeper->pwm), "Failed to request PWM device\n");
/* Sync up PWM state and ensure it is off. */
pwm_init_state(beeper->pwm, &state);
state.enabled = false;
- error = pwm_apply_state(beeper->pwm, &state);
+ error = pwm_apply_might_sleep(beeper->pwm, &state);
if (error) {
dev_err(dev, "failed to apply initial PWM state: %d\n",
error);
@@ -160,13 +146,9 @@ static int pwm_beeper_probe(struct platform_device *pdev)
}
beeper->amplifier = devm_regulator_get(dev, "amp");
- if (IS_ERR(beeper->amplifier)) {
- error = PTR_ERR(beeper->amplifier);
- if (error != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'amp' regulator: %d\n",
- error);
- return error;
- }
+ if (IS_ERR(beeper->amplifier))
+ return dev_err_probe(dev, PTR_ERR(beeper->amplifier),
+ "Failed to get 'amp' regulator\n");
INIT_WORK(&beeper->work, pwm_beeper_work);
@@ -212,7 +194,7 @@ static int pwm_beeper_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused pwm_beeper_suspend(struct device *dev)
+static int pwm_beeper_suspend(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
@@ -221,22 +203,22 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
* beeper->suspended, but to ensure that pwm_beeper_event
* does not re-submit work once flag is set.
*/
- spin_lock_irq(&beeper->input->event_lock);
- beeper->suspended = true;
- spin_unlock_irq(&beeper->input->event_lock);
+ scoped_guard(spinlock_irq, &beeper->input->event_lock) {
+ beeper->suspended = true;
+ }
pwm_beeper_stop(beeper);
return 0;
}
-static int __maybe_unused pwm_beeper_resume(struct device *dev)
+static int pwm_beeper_resume(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
- spin_lock_irq(&beeper->input->event_lock);
- beeper->suspended = false;
- spin_unlock_irq(&beeper->input->event_lock);
+ scoped_guard(spinlock_irq, &beeper->input->event_lock) {
+ beeper->suspended = false;
+ }
/* Let worker figure out if we should resume beeping */
schedule_work(&beeper->work);
@@ -244,8 +226,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
- pwm_beeper_suspend, pwm_beeper_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
+ pwm_beeper_suspend, pwm_beeper_resume);
#ifdef CONFIG_OF
static const struct of_device_id pwm_beeper_match[] = {
@@ -259,7 +241,7 @@ static struct platform_driver pwm_beeper_driver = {
.probe = pwm_beeper_probe,
.driver = {
.name = "pwm-beeper",
- .pm = &pwm_beeper_pm_ops,
+ .pm = pm_sleep_ptr(&pwm_beeper_pm_ops),
.of_match_table = of_match_ptr(pwm_beeper_match),
},
};
diff --git a/drivers/input/misc/pwm-vibra.c b/drivers/input/misc/pwm-vibra.c
index 55da191ae550..3e5ed685ed8f 100644
--- a/drivers/input/misc/pwm-vibra.c
+++ b/drivers/input/misc/pwm-vibra.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PWM vibrator driver
*
@@ -8,17 +9,13 @@
*
* Based on PWM beeper driver:
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/pwm.h>
@@ -27,6 +24,7 @@
struct pwm_vibrator {
struct input_dev *input;
+ struct gpio_desc *enable_gpio;
struct pwm_device *pwm;
struct pwm_device *pwm_dir;
struct regulator *vcc;
@@ -34,6 +32,7 @@ struct pwm_vibrator {
struct work_struct play_work;
u16 level;
u32 direction_duty_cycle;
+ bool vcc_on;
};
static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
@@ -42,19 +41,24 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
struct pwm_state state;
int err;
- err = regulator_enable(vibrator->vcc);
- if (err) {
- dev_err(pdev, "failed to enable regulator: %d", err);
- return err;
+ if (!vibrator->vcc_on) {
+ err = regulator_enable(vibrator->vcc);
+ if (err) {
+ dev_err(pdev, "failed to enable regulator: %d\n", err);
+ return err;
+ }
+ vibrator->vcc_on = true;
}
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
+
pwm_get_state(vibrator->pwm, &state);
pwm_set_relative_duty_cycle(&state, vibrator->level, 0xffff);
state.enabled = true;
- err = pwm_apply_state(vibrator->pwm, &state);
+ err = pwm_apply_might_sleep(vibrator->pwm, &state);
if (err) {
- dev_err(pdev, "failed to apply pwm state: %d", err);
+ dev_err(pdev, "failed to apply pwm state: %d\n", err);
return err;
}
@@ -63,9 +67,9 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
state.duty_cycle = vibrator->direction_duty_cycle;
state.enabled = true;
- err = pwm_apply_state(vibrator->pwm_dir, &state);
+ err = pwm_apply_might_sleep(vibrator->pwm_dir, &state);
if (err) {
- dev_err(pdev, "failed to apply dir-pwm state: %d", err);
+ dev_err(pdev, "failed to apply dir-pwm state: %d\n", err);
pwm_disable(vibrator->pwm);
return err;
}
@@ -76,11 +80,16 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
{
- regulator_disable(vibrator->vcc);
-
if (vibrator->pwm_dir)
pwm_disable(vibrator->pwm_dir);
pwm_disable(vibrator->pwm);
+
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
+
+ if (vibrator->vcc_on) {
+ regulator_disable(vibrator->vcc);
+ vibrator->vcc_on = false;
+ }
}
static void pwm_vibrator_play_work(struct work_struct *work)
@@ -131,31 +140,29 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
return -ENOMEM;
vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
- err = PTR_ERR_OR_ZERO(vibrator->vcc);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Failed to request regulator: %d",
- err);
- return err;
- }
+ if (IS_ERR(vibrator->vcc))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc),
+ "Failed to request regulator\n");
+
+ vibrator->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(vibrator->enable_gpio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->enable_gpio),
+ "Failed to request enable gpio\n");
vibrator->pwm = devm_pwm_get(&pdev->dev, "enable");
- err = PTR_ERR_OR_ZERO(vibrator->pwm);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Failed to request main pwm: %d",
- err);
- return err;
- }
+ if (IS_ERR(vibrator->pwm))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->pwm),
+ "Failed to request main pwm\n");
INIT_WORK(&vibrator->play_work, pwm_vibrator_play_work);
/* Sync up PWM state and ensure it is off. */
pwm_init_state(vibrator->pwm, &state);
state.enabled = false;
- err = pwm_apply_state(vibrator->pwm, &state);
+ err = pwm_apply_might_sleep(vibrator->pwm, &state);
if (err) {
- dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+ dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
err);
return err;
}
@@ -167,9 +174,9 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
/* Sync up PWM state and ensure it is off. */
pwm_init_state(vibrator->pwm_dir, &state);
state.enabled = false;
- err = pwm_apply_state(vibrator->pwm_dir, &state);
+ err = pwm_apply_might_sleep(vibrator->pwm_dir, &state);
if (err) {
- dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+ dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
err);
return err;
}
@@ -186,8 +193,8 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
break;
default:
- dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
- /* Fall through */
+ dev_err(&pdev->dev, "Failed to request direction pwm: %d\n", err);
+ fallthrough;
case -EPROBE_DEFER:
return err;
@@ -204,13 +211,13 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
err = input_ff_create_memless(vibrator->input, NULL,
pwm_vibrator_play_effect);
if (err) {
- dev_err(&pdev->dev, "Couldn't create FF dev: %d", err);
+ dev_err(&pdev->dev, "Couldn't create FF dev: %d\n", err);
return err;
}
err = input_register_device(vibrator->input);
if (err) {
- dev_err(&pdev->dev, "Couldn't register input dev: %d", err);
+ dev_err(&pdev->dev, "Couldn't register input dev: %d\n", err);
return err;
}
@@ -219,7 +226,7 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused pwm_vibrator_suspend(struct device *dev)
+static int pwm_vibrator_suspend(struct device *dev)
{
struct pwm_vibrator *vibrator = dev_get_drvdata(dev);
@@ -230,7 +237,7 @@ static int __maybe_unused pwm_vibrator_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused pwm_vibrator_resume(struct device *dev)
+static int pwm_vibrator_resume(struct device *dev)
{
struct pwm_vibrator *vibrator = dev_get_drvdata(dev);
@@ -240,8 +247,8 @@ static int __maybe_unused pwm_vibrator_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pwm_vibrator_pm_ops,
- pwm_vibrator_suspend, pwm_vibrator_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pwm_vibrator_pm_ops,
+ pwm_vibrator_suspend, pwm_vibrator_resume);
#ifdef CONFIG_OF
static const struct of_device_id pwm_vibra_dt_match_table[] = {
@@ -255,7 +262,7 @@ static struct platform_driver pwm_vibrator_driver = {
.probe = pwm_vibrator_probe,
.driver = {
.name = "pwm-vibrator",
- .pm = &pwm_vibrator_pm_ops,
+ .pm = pm_sleep_ptr(&pwm_vibrator_pm_ops),
.of_match_table = of_match_ptr(pwm_vibra_dt_match_table),
},
};
diff --git a/drivers/input/misc/qnap-mcu-input.c b/drivers/input/misc/qnap-mcu-input.c
new file mode 100644
index 000000000000..76e62f0816c1
--- /dev/null
+++ b/drivers/input/misc/qnap-mcu-input.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Driver for input events on QNAP-MCUs
+ *
+ * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
+ */
+
+#include <linux/input.h>
+#include <linux/mfd/qnap-mcu.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <uapi/linux/input-event-codes.h>
+
+/*
+ * The power-key needs to be pressed for a while to create an event,
+ * so there is no use for overly frequent polling.
+ */
+#define POLL_INTERVAL 500
+
+struct qnap_mcu_input_dev {
+ struct input_dev *input;
+ struct qnap_mcu *mcu;
+ struct device *dev;
+
+ struct work_struct beep_work;
+ int beep_type;
+};
+
+static void qnap_mcu_input_poll(struct input_dev *input)
+{
+ struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
+ static const u8 cmd[] = { '@', 'C', 'V' };
+ u8 reply[4];
+ int state, ret;
+
+ /* poll the power button */
+ ret = qnap_mcu_exec(idev->mcu, cmd, sizeof(cmd), reply, sizeof(reply));
+ if (ret)
+ return;
+
+ /* First bytes must mirror the sent command */
+ if (memcmp(cmd, reply, sizeof(cmd))) {
+ dev_err(idev->dev, "malformed data received\n");
+ return;
+ }
+
+ state = reply[3] - 0x30;
+ input_event(input, EV_KEY, KEY_POWER, state);
+ input_sync(input);
+}
+
+static void qnap_mcu_input_beeper_work(struct work_struct *work)
+{
+ struct qnap_mcu_input_dev *idev =
+ container_of(work, struct qnap_mcu_input_dev, beep_work);
+ const u8 cmd[] = { '@', 'C', (idev->beep_type == SND_TONE) ? '3' : '2' };
+
+ qnap_mcu_exec_with_ack(idev->mcu, cmd, sizeof(cmd));
+}
+
+static int qnap_mcu_input_event(struct input_dev *input, unsigned int type,
+ unsigned int code, int value)
+{
+ struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
+
+ if (type != EV_SND || (code != SND_BELL && code != SND_TONE))
+ return -EOPNOTSUPP;
+
+ if (value < 0)
+ return -EINVAL;
+
+ /* beep runtime is determined by the MCU */
+ if (value == 0)
+ return 0;
+
+ /* Schedule work to actually turn the beeper on */
+ idev->beep_type = code;
+ schedule_work(&idev->beep_work);
+
+ return 0;
+}
+
+static void qnap_mcu_input_close(struct input_dev *input)
+{
+ struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
+
+ cancel_work_sync(&idev->beep_work);
+}
+
+static int qnap_mcu_input_probe(struct platform_device *pdev)
+{
+ struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
+ struct qnap_mcu_input_dev *idev;
+ struct device *dev = &pdev->dev;
+ struct input_dev *input;
+ int ret;
+
+ idev = devm_kzalloc(dev, sizeof(*idev), GFP_KERNEL);
+ if (!idev)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return dev_err_probe(dev, -ENOMEM, "no memory for input device\n");
+
+ idev->input = input;
+ idev->dev = dev;
+ idev->mcu = mcu;
+
+ input_set_drvdata(input, idev);
+
+ input->name = "qnap-mcu";
+ input->phys = "qnap-mcu-input/input0";
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+ input->event = qnap_mcu_input_event;
+ input->close = qnap_mcu_input_close;
+
+ input_set_capability(input, EV_KEY, KEY_POWER);
+ input_set_capability(input, EV_SND, SND_BELL);
+ input_set_capability(input, EV_SND, SND_TONE);
+
+ INIT_WORK(&idev->beep_work, qnap_mcu_input_beeper_work);
+
+ ret = input_setup_polling(input, qnap_mcu_input_poll);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to set up polling\n");
+
+ input_set_poll_interval(input, POLL_INTERVAL);
+
+ ret = input_register_device(input);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to register input device\n");
+
+ return 0;
+}
+
+static struct platform_driver qnap_mcu_input_driver = {
+ .probe = qnap_mcu_input_probe,
+ .driver = {
+ .name = "qnap-mcu-input",
+ },
+};
+module_platform_driver(qnap_mcu_input_driver);
+
+MODULE_ALIAS("platform:qnap-mcu-input");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("QNAP MCU input driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
index 62c5814c796b..190a80e1e2c1 100644
--- a/drivers/input/misc/rb532_button.c
+++ b/drivers/input/misc/rb532_button.c
@@ -1,10 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Support for the S1 button on Routerboard 532
*
* Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
*/
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
@@ -45,56 +46,42 @@ static bool rb532_button_pressed(void)
return !val;
}
-static void rb532_button_poll(struct input_polled_dev *poll_dev)
+static void rb532_button_poll(struct input_dev *input)
{
- input_report_key(poll_dev->input, RB532_BTN_KSYM,
- rb532_button_pressed());
- input_sync(poll_dev->input);
+ input_report_key(input, RB532_BTN_KSYM, rb532_button_pressed());
+ input_sync(input);
}
static int rb532_button_probe(struct platform_device *pdev)
{
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
int error;
- poll_dev = input_allocate_polled_device();
- if (!poll_dev)
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
return -ENOMEM;
- poll_dev->poll = rb532_button_poll;
- poll_dev->poll_interval = RB532_BTN_RATE;
+ input->name = "rb532 button";
+ input->phys = "rb532/button0";
+ input->id.bustype = BUS_HOST;
- poll_dev->input->name = "rb532 button";
- poll_dev->input->phys = "rb532/button0";
- poll_dev->input->id.bustype = BUS_HOST;
- poll_dev->input->dev.parent = &pdev->dev;
+ input_set_capability(input, EV_KEY, RB532_BTN_KSYM);
- dev_set_drvdata(&pdev->dev, poll_dev);
-
- input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
-
- error = input_register_polled_device(poll_dev);
- if (error) {
- input_free_polled_device(poll_dev);
+ error = input_setup_polling(input, rb532_button_poll);
+ if (error)
return error;
- }
- return 0;
-}
+ input_set_poll_interval(input, RB532_BTN_RATE);
-static int rb532_button_remove(struct platform_device *pdev)
-{
- struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
-
- input_unregister_polled_device(poll_dev);
- input_free_polled_device(poll_dev);
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
static struct platform_driver rb532_button_driver = {
.probe = rb532_button_probe,
- .remove = rb532_button_remove,
.driver = {
.name = DRV_NAME,
},
diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c
index a1db1e5040dc..9711f5c7c78a 100644
--- a/drivers/input/misc/regulator-haptic.c
+++ b/drivers/input/misc/regulator-haptic.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Regulator haptic driver
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Jaewon Kim <jaewon02.kim@samsung.com>
* Author: Hyunhee Kim <hyunhee.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/input.h>
@@ -17,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#define MAX_MAGNITUDE_SHIFT 16
@@ -47,7 +45,7 @@ static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
if (error) {
dev_err(haptic->dev,
"failed to switch regulator %s: %d\n",
- on ? "on" : "off", error);
+ str_on_off(on), error);
return error;
}
@@ -86,12 +84,10 @@ static void regulator_haptic_work(struct work_struct *work)
struct regulator_haptic *haptic = container_of(work,
struct regulator_haptic, work);
- mutex_lock(&haptic->mutex);
+ guard(mutex)(&haptic->mutex);
if (!haptic->suspended)
regulator_haptic_set_voltage(haptic, haptic->magnitude);
-
- mutex_unlock(&haptic->mutex);
}
static int regulator_haptic_play_effect(struct input_dev *input, void *data,
@@ -204,32 +200,28 @@ static int regulator_haptic_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused regulator_haptic_suspend(struct device *dev)
+static int regulator_haptic_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
- int error;
-
- error = mutex_lock_interruptible(&haptic->mutex);
- if (error)
- return error;
- regulator_haptic_set_voltage(haptic, 0);
-
- haptic->suspended = true;
+ scoped_guard(mutex_intr, &haptic->mutex) {
+ regulator_haptic_set_voltage(haptic, 0);
+ haptic->suspended = true;
- mutex_unlock(&haptic->mutex);
+ return 0;
+ }
- return 0;
+ return -EINTR;
}
-static int __maybe_unused regulator_haptic_resume(struct device *dev)
+static int regulator_haptic_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
unsigned int magnitude;
- mutex_lock(&haptic->mutex);
+ guard(mutex)(&haptic->mutex);
haptic->suspended = false;
@@ -237,12 +229,10 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev)
if (magnitude)
regulator_haptic_set_voltage(haptic, magnitude);
- mutex_unlock(&haptic->mutex);
-
return 0;
}
-static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
regulator_haptic_suspend, regulator_haptic_resume);
static const struct of_device_id regulator_haptic_dt_match[] = {
@@ -256,7 +246,7 @@ static struct platform_driver regulator_haptic_driver = {
.driver = {
.name = "regulator-haptic",
.of_match_table = regulator_haptic_dt_match,
- .pm = &regulator_haptic_pm_ops,
+ .pm = pm_sleep_ptr(&regulator_haptic_pm_ops),
},
};
module_platform_driver(regulator_haptic_driver);
diff --git a/drivers/input/misc/rk805-pwrkey.c b/drivers/input/misc/rk805-pwrkey.c
index 921003963a53..76873aa005b4 100644
--- a/drivers/input/misc/rk805-pwrkey.c
+++ b/drivers/input/misc/rk805-pwrkey.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip RK805 PMIC Power Key driver
*
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Joseph Chen <chenjh@rock-chips.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/errno.h>
@@ -57,16 +53,12 @@ static int rk805_pwrkey_probe(struct platform_device *pdev)
input_set_capability(pwr, EV_KEY, KEY_POWER);
fall_irq = platform_get_irq(pdev, 0);
- if (fall_irq < 0) {
- dev_err(&pdev->dev, "Can't get fall irq: %d\n", fall_irq);
+ if (fall_irq < 0)
return fall_irq;
- }
rise_irq = platform_get_irq(pdev, 1);
- if (rise_irq < 0) {
- dev_err(&pdev->dev, "Can't get rise irq: %d\n", rise_irq);
+ if (rise_irq < 0)
return rise_irq;
- }
err = devm_request_any_context_irq(&pwr->dev, fall_irq,
pwrkey_fall_irq,
@@ -106,6 +98,7 @@ static struct platform_driver rk805_pwrkey_driver = {
};
module_platform_driver(rk805_pwrkey_driver);
+MODULE_ALIAS("platform:rk805-pwrkey");
MODULE_AUTHOR("Joseph Chen <chenjh@rock-chips.com>");
MODULE_DESCRIPTION("RK805 PMIC Power Key driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index d748897bf5e9..f706e1997417 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rotary_encoder.c
*
@@ -8,10 +9,6 @@
*
* A generic driver for rotary encoders connected to GPIO lines.
* See file:Documentation/input/devices/rotary-encoder.rst for more information
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -109,7 +106,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
struct rotary_encoder *encoder = dev_id;
unsigned int state;
- mutex_lock(&encoder->access_mutex);
+ guard(mutex)(&encoder->access_mutex);
state = rotary_encoder_get_state(encoder);
@@ -132,8 +129,6 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
break;
}
- mutex_unlock(&encoder->access_mutex);
-
return IRQ_HANDLED;
}
@@ -142,7 +137,7 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
struct rotary_encoder *encoder = dev_id;
unsigned int state;
- mutex_lock(&encoder->access_mutex);
+ guard(mutex)(&encoder->access_mutex);
state = rotary_encoder_get_state(encoder);
@@ -155,8 +150,6 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
}
}
- mutex_unlock(&encoder->access_mutex);
-
return IRQ_HANDLED;
}
@@ -165,22 +158,19 @@ static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
struct rotary_encoder *encoder = dev_id;
unsigned int state;
- mutex_lock(&encoder->access_mutex);
+ guard(mutex)(&encoder->access_mutex);
state = rotary_encoder_get_state(encoder);
- if ((encoder->last_stable + 1) % 4 == state)
+ if ((encoder->last_stable + 1) % 4 == state) {
encoder->dir = 1;
- else if (encoder->last_stable == (state + 1) % 4)
+ rotary_encoder_report_event(encoder);
+ } else if (encoder->last_stable == (state + 1) % 4) {
encoder->dir = -1;
- else
- goto out;
-
- rotary_encoder_report_event(encoder);
+ rotary_encoder_report_event(encoder);
+ }
-out:
encoder->last_stable = state;
- mutex_unlock(&encoder->access_mutex);
return IRQ_HANDLED;
}
@@ -239,12 +229,8 @@ static int rotary_encoder_probe(struct platform_device *pdev)
device_property_read_bool(dev, "rotary-encoder,relative-axis");
encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN);
- if (IS_ERR(encoder->gpios)) {
- err = PTR_ERR(encoder->gpios);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "unable to get gpios: %d\n", err);
- return err;
- }
+ if (IS_ERR(encoder->gpios))
+ return dev_err_probe(dev, PTR_ERR(encoder->gpios), "unable to get gpios\n");
if (encoder->gpios->ndescs < 2) {
dev_err(dev, "not enough gpios found\n");
return -EINVAL;
@@ -258,7 +244,6 @@ static int rotary_encoder_probe(struct platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
- input->dev.parent = dev;
if (encoder->relative_axis)
input_set_capability(input, EV_REL, encoder->axis);
@@ -320,7 +305,7 @@ static int rotary_encoder_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused rotary_encoder_suspend(struct device *dev)
+static int rotary_encoder_suspend(struct device *dev)
{
struct rotary_encoder *encoder = dev_get_drvdata(dev);
unsigned int i;
@@ -333,7 +318,7 @@ static int __maybe_unused rotary_encoder_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused rotary_encoder_resume(struct device *dev)
+static int rotary_encoder_resume(struct device *dev)
{
struct rotary_encoder *encoder = dev_get_drvdata(dev);
unsigned int i;
@@ -346,8 +331,8 @@ static int __maybe_unused rotary_encoder_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
- rotary_encoder_suspend, rotary_encoder_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
+ rotary_encoder_suspend, rotary_encoder_resume);
#ifdef CONFIG_OF
static const struct of_device_id rotary_encoder_of_match[] = {
@@ -361,7 +346,7 @@ static struct platform_driver rotary_encoder_driver = {
.probe = rotary_encoder_probe,
.driver = {
.name = DRV_NAME,
- .pm = &rotary_encoder_pm_ops,
+ .pm = pm_sleep_ptr(&rotary_encoder_pm_ops),
.of_match_table = of_match_ptr(rotary_encoder_of_match),
}
};
diff --git a/drivers/input/misc/rt5120-pwrkey.c b/drivers/input/misc/rt5120-pwrkey.c
new file mode 100644
index 000000000000..8a8c1aeeed05
--- /dev/null
+++ b/drivers/input/misc/rt5120-pwrkey.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Richtek Technology Corp.
+ * Author: ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define RT5120_REG_INTSTAT 0x1E
+#define RT5120_PWRKEYSTAT_MASK BIT(7)
+
+struct rt5120_priv {
+ struct regmap *regmap;
+ struct input_dev *input;
+};
+
+static irqreturn_t rt5120_pwrkey_handler(int irq, void *devid)
+{
+ struct rt5120_priv *priv = devid;
+ unsigned int stat;
+ int error;
+
+ error = regmap_read(priv->regmap, RT5120_REG_INTSTAT, &stat);
+ if (error)
+ return IRQ_NONE;
+
+ input_report_key(priv->input, KEY_POWER,
+ !(stat & RT5120_PWRKEYSTAT_MASK));
+ input_sync(priv->input);
+
+ return IRQ_HANDLED;
+}
+
+static int rt5120_pwrkey_probe(struct platform_device *pdev)
+{
+ struct rt5120_priv *priv;
+ struct device *dev = &pdev->dev;
+ int press_irq, release_irq;
+ int error;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!priv->regmap) {
+ dev_err(dev, "Failed to init regmap\n");
+ return -ENODEV;
+ }
+
+ press_irq = platform_get_irq_byname(pdev, "pwrkey-press");
+ if (press_irq < 0)
+ return press_irq;
+
+ release_irq = platform_get_irq_byname(pdev, "pwrkey-release");
+ if (release_irq < 0)
+ return release_irq;
+
+ /* Make input device be device resource managed */
+ priv->input = devm_input_allocate_device(dev);
+ if (!priv->input)
+ return -ENOMEM;
+
+ priv->input->name = "rt5120_pwrkey";
+ priv->input->phys = "rt5120_pwrkey/input0";
+ priv->input->id.bustype = BUS_I2C;
+ input_set_capability(priv->input, EV_KEY, KEY_POWER);
+
+ error = input_register_device(priv->input);
+ if (error) {
+ dev_err(dev, "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, press_irq,
+ NULL, rt5120_pwrkey_handler,
+ 0, "pwrkey-press", priv);
+ if (error) {
+ dev_err(dev,
+ "Failed to register pwrkey press irq: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, release_irq,
+ NULL, rt5120_pwrkey_handler,
+ 0, "pwrkey-release", priv);
+ if (error) {
+ dev_err(dev,
+ "Failed to register pwrkey release irq: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id r5120_pwrkey_match_table[] = {
+ { .compatible = "richtek,rt5120-pwrkey" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, r5120_pwrkey_match_table);
+
+static struct platform_driver rt5120_pwrkey_driver = {
+ .driver = {
+ .name = "rt5120-pwrkey",
+ .of_match_table = r5120_pwrkey_match_table,
+ },
+ .probe = rt5120_pwrkey_probe,
+};
+module_platform_driver(rt5120_pwrkey_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT5120 power key driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sc27xx-vibra.c b/drivers/input/misc/sc27xx-vibra.c
index 295251abbdac..1478017f0968 100644
--- a/drivers/input/misc/sc27xx-vibra.c
+++ b/drivers/input/misc/sc27xx-vibra.c
@@ -3,45 +3,82 @@
* Copyright (C) 2018 Spreadtrum Communications Inc.
*/
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <linux/input.h>
#include <linux/workqueue.h>
-#define CUR_DRV_CAL_SEL GENMASK(13, 12)
-#define SLP_LDOVIBR_PD_EN BIT(9)
-#define LDO_VIBR_PD BIT(8)
+#define CUR_DRV_CAL_SEL GENMASK(13, 12)
+#define SLP_LDOVIBR_PD_EN BIT(9)
+#define LDO_VIBR_PD BIT(8)
+#define SC2730_CUR_DRV_CAL_SEL 0
+#define SC2730_SLP_LDOVIBR_PD_EN BIT(14)
+#define SC2730_LDO_VIBR_PD BIT(13)
+
+struct sc27xx_vibra_data {
+ u32 cur_drv_cal_sel;
+ u32 slp_pd_en;
+ u32 ldo_pd;
+};
struct vibra_info {
struct input_dev *input_dev;
struct work_struct play_work;
struct regmap *regmap;
+ const struct sc27xx_vibra_data *data;
u32 base;
u32 strength;
bool enabled;
};
+static const struct sc27xx_vibra_data sc2731_data = {
+ .cur_drv_cal_sel = CUR_DRV_CAL_SEL,
+ .slp_pd_en = SLP_LDOVIBR_PD_EN,
+ .ldo_pd = LDO_VIBR_PD,
+};
+
+static const struct sc27xx_vibra_data sc2730_data = {
+ .cur_drv_cal_sel = SC2730_CUR_DRV_CAL_SEL,
+ .slp_pd_en = SC2730_SLP_LDOVIBR_PD_EN,
+ .ldo_pd = SC2730_LDO_VIBR_PD,
+};
+
+static const struct sc27xx_vibra_data sc2721_data = {
+ .cur_drv_cal_sel = CUR_DRV_CAL_SEL,
+ .slp_pd_en = SLP_LDOVIBR_PD_EN,
+ .ldo_pd = LDO_VIBR_PD,
+};
+
static void sc27xx_vibra_set(struct vibra_info *info, bool on)
{
+ const struct sc27xx_vibra_data *data = info->data;
if (on) {
- regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, 0);
+ regmap_update_bits(info->regmap, info->base, data->ldo_pd, 0);
regmap_update_bits(info->regmap, info->base,
- SLP_LDOVIBR_PD_EN, 0);
+ data->slp_pd_en, 0);
info->enabled = true;
} else {
- regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD,
- LDO_VIBR_PD);
+ regmap_update_bits(info->regmap, info->base, data->ldo_pd,
+ data->ldo_pd);
regmap_update_bits(info->regmap, info->base,
- SLP_LDOVIBR_PD_EN, SLP_LDOVIBR_PD_EN);
+ data->slp_pd_en, data->slp_pd_en);
info->enabled = false;
}
}
static int sc27xx_vibra_hw_init(struct vibra_info *info)
{
- return regmap_update_bits(info->regmap, info->base, CUR_DRV_CAL_SEL, 0);
+ const struct sc27xx_vibra_data *data = info->data;
+
+ if (!data->cur_drv_cal_sel)
+ return 0;
+
+ return regmap_update_bits(info->regmap, info->base,
+ data->cur_drv_cal_sel, 0);
}
static void sc27xx_vibra_play_work(struct work_struct *work)
@@ -78,8 +115,15 @@ static void sc27xx_vibra_close(struct input_dev *input)
static int sc27xx_vibra_probe(struct platform_device *pdev)
{
struct vibra_info *info;
+ const struct sc27xx_vibra_data *data;
int error;
+ data = device_get_match_data(&pdev->dev);
+ if (!data) {
+ dev_err(&pdev->dev, "no matching driver data found\n");
+ return -EINVAL;
+ }
+
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -105,6 +149,7 @@ static int sc27xx_vibra_probe(struct platform_device *pdev)
info->input_dev->name = "sc27xx:vibrator";
info->input_dev->id.version = 0;
info->input_dev->close = sc27xx_vibra_close;
+ info->data = data;
input_set_drvdata(info->input_dev, info);
input_set_capability(info->input_dev, EV_FF, FF_RUMBLE);
@@ -134,7 +179,9 @@ static int sc27xx_vibra_probe(struct platform_device *pdev)
}
static const struct of_device_id sc27xx_vibra_of_match[] = {
- { .compatible = "sprd,sc2731-vibrator", },
+ { .compatible = "sprd,sc2721-vibrator", .data = &sc2721_data },
+ { .compatible = "sprd,sc2730-vibrator", .data = &sc2730_data },
+ { .compatible = "sprd,sc2731-vibrator", .data = &sc2731_data },
{}
};
MODULE_DEVICE_TABLE(of, sc27xx_vibra_of_match);
diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c
index 7bbe79d89f5c..39c2882b8e1a 100644
--- a/drivers/input/misc/sgi_btns.c
+++ b/drivers/input/misc/sgi_btns.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SGI Volume Button interface driver
*
* Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -58,15 +45,13 @@ static const unsigned short sgi_map[] = {
};
struct buttons_dev {
- struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(sgi_map)];
int count[ARRAY_SIZE(sgi_map)];
};
-static void handle_buttons(struct input_polled_dev *dev)
+static void handle_buttons(struct input_dev *input)
{
- struct buttons_dev *bdev = dev->private;
- struct input_dev *input = dev->input;
+ struct buttons_dev *bdev = input_get_drvdata(input);
u8 status;
int i;
@@ -93,28 +78,24 @@ static void handle_buttons(struct input_polled_dev *dev)
static int sgi_buttons_probe(struct platform_device *pdev)
{
struct buttons_dev *bdev;
- struct input_polled_dev *poll_dev;
struct input_dev *input;
int error, i;
- bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
- poll_dev = input_allocate_polled_device();
- if (!bdev || !poll_dev) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return -ENOMEM;
memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
- poll_dev->private = bdev;
- poll_dev->poll = handle_buttons;
- poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+ input_set_drvdata(input, bdev);
- input = poll_dev->input;
input->name = "SGI buttons";
input->phys = "sgi/input0";
input->id.bustype = BUS_HOST;
- input->dev.parent = &pdev->dev;
input->keycode = bdev->keymap;
input->keycodemax = ARRAY_SIZE(bdev->keymap);
@@ -126,39 +107,26 @@ static int sgi_buttons_probe(struct platform_device *pdev)
__set_bit(bdev->keymap[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
- bdev->poll_dev = poll_dev;
- platform_set_drvdata(pdev, bdev);
-
- error = input_register_polled_device(poll_dev);
+ error = input_setup_polling(input, handle_buttons);
if (error)
- goto err_free_mem;
+ return error;
- return 0;
-
- err_free_mem:
- input_free_polled_device(poll_dev);
- kfree(bdev);
- return error;
-}
+ input_set_poll_interval(input, BUTTONS_POLL_INTERVAL);
-static int sgi_buttons_remove(struct platform_device *pdev)
-{
- struct buttons_dev *bdev = platform_get_drvdata(pdev);
-
- input_unregister_polled_device(bdev->poll_dev);
- input_free_polled_device(bdev->poll_dev);
- kfree(bdev);
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
static struct platform_driver sgi_buttons_driver = {
.probe = sgi_buttons_probe,
- .remove = sgi_buttons_remove,
.driver = {
.name = "sgibtns",
},
};
module_platform_driver(sgi_buttons_driver);
+MODULE_DESCRIPTION("SGI Indy/O2 volume button interface driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c
deleted file mode 100644
index 4fd038d476a3..000000000000
--- a/drivers/input/misc/sirfsoc-onkey.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Power key driver for SiRF PrimaII
- *
- * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
- * company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-#include <linux/rtc/sirfsoc_rtciobrg.h>
-#include <linux/of.h>
-#include <linux/workqueue.h>
-
-struct sirfsoc_pwrc_drvdata {
- u32 pwrc_base;
- struct input_dev *input;
- struct delayed_work work;
-};
-
-#define PWRC_ON_KEY_BIT (1 << 0)
-
-#define PWRC_INT_STATUS 0xc
-#define PWRC_INT_MASK 0x10
-#define PWRC_PIN_STATUS 0x14
-#define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/
-
-static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv)
-{
- u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
- PWRC_PIN_STATUS);
- return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */
-}
-
-static void sirfsoc_pwrc_report_event(struct work_struct *work)
-{
- struct sirfsoc_pwrc_drvdata *pwrcdrv =
- container_of(work, struct sirfsoc_pwrc_drvdata, work.work);
-
- if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) {
- schedule_delayed_work(&pwrcdrv->work,
- msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
- } else {
- input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0);
- input_sync(pwrcdrv->input);
- }
-}
-
-static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
-{
- struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_id;
- u32 int_status;
-
- int_status = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
- PWRC_INT_STATUS);
- sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT,
- pwrcdrv->pwrc_base + PWRC_INT_STATUS);
-
- input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1);
- input_sync(pwrcdrv->input);
- schedule_delayed_work(&pwrcdrv->work,
- msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
-
- return IRQ_HANDLED;
-}
-
-static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv,
- bool enable)
-{
- u32 int_mask;
-
- int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK);
- if (enable)
- int_mask |= PWRC_ON_KEY_BIT;
- else
- int_mask &= ~PWRC_ON_KEY_BIT;
- sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK);
-}
-
-static int sirfsoc_pwrc_open(struct input_dev *input)
-{
- struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
-
- sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
-
- return 0;
-}
-
-static void sirfsoc_pwrc_close(struct input_dev *input)
-{
- struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
-
- sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
- cancel_delayed_work_sync(&pwrcdrv->work);
-}
-
-static const struct of_device_id sirfsoc_pwrc_of_match[] = {
- { .compatible = "sirf,prima2-pwrc" },
- {},
-};
-MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match);
-
-static int sirfsoc_pwrc_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct sirfsoc_pwrc_drvdata *pwrcdrv;
- int irq;
- int error;
-
- pwrcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_pwrc_drvdata),
- GFP_KERNEL);
- if (!pwrcdrv) {
- dev_info(&pdev->dev, "Not enough memory for the device data\n");
- return -ENOMEM;
- }
-
- /*
- * We can't use of_iomap because pwrc is not mapped in memory,
- * the so-called base address is only offset in rtciobrg
- */
- error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base);
- if (error) {
- dev_err(&pdev->dev,
- "unable to find base address of pwrc node in dtb\n");
- return error;
- }
-
- pwrcdrv->input = devm_input_allocate_device(&pdev->dev);
- if (!pwrcdrv->input)
- return -ENOMEM;
-
- pwrcdrv->input->name = "sirfsoc pwrckey";
- pwrcdrv->input->phys = "pwrc/input0";
- pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY);
- input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER);
-
- INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event);
-
- pwrcdrv->input->open = sirfsoc_pwrc_open;
- pwrcdrv->input->close = sirfsoc_pwrc_close;
-
- input_set_drvdata(pwrcdrv->input, pwrcdrv);
-
- /* Make sure the device is quiesced */
- sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
-
- irq = platform_get_irq(pdev, 0);
- error = devm_request_irq(&pdev->dev, irq,
- sirfsoc_pwrc_isr, 0,
- "sirfsoc_pwrc_int", pwrcdrv);
- if (error) {
- dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n",
- irq, error);
- return error;
- }
-
- error = input_register_device(pwrcdrv->input);
- if (error) {
- dev_err(&pdev->dev,
- "unable to register input device, error: %d\n",
- error);
- return error;
- }
-
- dev_set_drvdata(&pdev->dev, pwrcdrv);
- device_init_wakeup(&pdev->dev, 1);
-
- return 0;
-}
-
-static int __maybe_unused sirfsoc_pwrc_resume(struct device *dev)
-{
- struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev);
- struct input_dev *input = pwrcdrv->input;
-
- /*
- * Do not mask pwrc interrupt as we want pwrc work as a wakeup source
- * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c
- */
- mutex_lock(&input->mutex);
- if (input->users)
- sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
- mutex_unlock(&input->mutex);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume);
-
-static struct platform_driver sirfsoc_pwrc_driver = {
- .probe = sirfsoc_pwrc_probe,
- .driver = {
- .name = "sirfsoc-pwrc",
- .pm = &sirfsoc_pwrc_pm_ops,
- .of_match_table = sirfsoc_pwrc_of_match,
- }
-};
-
-module_platform_driver(sirfsoc_pwrc_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com>");
-MODULE_DESCRIPTION("CSR Prima2 PWRC Driver");
-MODULE_ALIAS("platform:sirfsoc-pwrc");
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index 23520df7650f..b8cad415c62c 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -1,25 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Supports for the button array on SoC tablets originally running
* Windows 8.
*
* (C) Copyright 2014 Intel Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+static bool use_low_level_irq;
+module_param(use_low_level_irq, bool, 0444);
+MODULE_PARM_DESC(use_low_level_irq, "Use low-level triggered IRQ instead of edge triggered");
+
struct soc_button_info {
const char *name;
int acpi_index;
@@ -27,6 +29,12 @@ struct soc_button_info {
unsigned int event_code;
bool autorepeat;
bool wakeup;
+ bool active_low;
+};
+
+struct soc_device_data {
+ const struct soc_button_info *button_info;
+ int (*check)(struct device *dev);
};
/*
@@ -41,22 +49,104 @@ struct soc_button_data {
};
/*
+ * Some 2-in-1s which use the soc_button_array driver have this ugly issue in
+ * their DSDT where the _LID method modifies the irq-type settings of the GPIOs
+ * used for the power and home buttons. The intend of this AML code is to
+ * disable these buttons when the lid is closed.
+ * The AML does this by directly poking the GPIO controllers registers. This is
+ * problematic because when re-enabling the irq, which happens whenever _LID
+ * gets called with the lid open (e.g. on boot and on resume), it sets the
+ * irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the
+ * type to, and expects it to be, IRQ_TYPE_EDGE_BOTH.
+ * To work around this we don't set gpio_keys_button.gpio on these 2-in-1s,
+ * instead we get the irq for the GPIO ourselves, configure it as
+ * IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass
+ * the irq in gpio_keys_button.irq. Below is a list of affected devices.
+ */
+static const struct dmi_system_id dmi_use_low_level_irq[] = {
+ {
+ /*
+ * Acer Switch 10 SW5-012. _LID method messes with home- and
+ * power-button GPIO IRQ settings. When (re-)enabling the irq
+ * it ors in its own flags without clearing the previous set
+ * ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW |
+ * IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
+ },
+ },
+ {
+ /* Acer Switch V 10 SW5-017, same issue as Acer Switch 10 SW5-012. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
+ },
+ },
+ {
+ /*
+ * Acer One S1003. _LID method messes with power-button GPIO
+ * IRQ settings, leading to a non working power-button.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"),
+ },
+ },
+ {
+ /*
+ * Lenovo Yoga Tab2 1051F/1051L, something messes with the home-button
+ * IRQ settings, leading to a non working home-button.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "60073"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1051"),
+ },
+ },
+ {} /* Terminating entry */
+};
+
+/*
+ * Some devices have a wrong entry which points to a GPIO which is
+ * required in another driver, so this driver must not claim it.
+ */
+static const struct dmi_system_id dmi_invalid_acpi_index[] = {
+ {
+ /*
+ * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry
+ * points to a GPIO which is not a home button and which is
+ * required by the lenovo-yogabook driver.
+ */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
+ },
+ .driver_data = (void *)1l,
+ },
+ {} /* Terminating entry */
+};
+
+/*
* Get the Nth GPIO number from the ACPI object.
*/
-static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
+static int soc_button_lookup_gpio(struct device *dev, int acpi_index,
+ int *gpio_ret, int *irq_ret)
{
struct gpio_desc *desc;
- int gpio;
desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
if (IS_ERR(desc))
return PTR_ERR(desc);
- gpio = desc_to_gpio(desc);
+ *gpio_ret = desc_to_gpio(desc);
+ *irq_ret = gpiod_to_irq(desc);
gpiod_put(desc);
- return gpio;
+ return 0;
}
static struct platform_device *
@@ -68,9 +158,10 @@ soc_button_device_create(struct platform_device *pdev,
struct platform_device *pd;
struct gpio_keys_button *gpio_keys;
struct gpio_keys_platform_data *gpio_keys_pdata;
+ const struct dmi_system_id *dmi_id;
+ int invalid_acpi_index = -1;
+ int error, gpio, irq;
int n_buttons = 0;
- int gpio;
- int error;
for (info = button_info; info->name; info++)
if (info->autorepeat == autorepeat)
@@ -86,18 +177,46 @@ soc_button_device_create(struct platform_device *pdev,
gpio_keys = (void *)(gpio_keys_pdata + 1);
n_buttons = 0;
+ dmi_id = dmi_first_match(dmi_invalid_acpi_index);
+ if (dmi_id)
+ invalid_acpi_index = (long)dmi_id->driver_data;
+
for (info = button_info; info->name; info++) {
if (info->autorepeat != autorepeat)
continue;
- gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
- if (!gpio_is_valid(gpio))
+ if (info->acpi_index == invalid_acpi_index)
continue;
+ error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq);
+ if (error || irq < 0) {
+ /*
+ * Skip GPIO if not present. Note we deliberately
+ * ignore -EPROBE_DEFER errors here. On some devices
+ * Intel is using so called virtual GPIOs which are not
+ * GPIOs at all but some way for AML code to check some
+ * random status bits without need a custom opregion.
+ * In some cases the resources table we parse points to
+ * such a virtual GPIO, since these are not real GPIOs
+ * we do not have a driver for these so they will never
+ * show up, therefore we ignore -EPROBE_DEFER.
+ */
+ continue;
+ }
+
+ /* See dmi_use_low_level_irq[] comment */
+ if (!autorepeat && (use_low_level_irq ||
+ dmi_check_system(dmi_use_low_level_irq))) {
+ irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+ gpio_keys[n_buttons].irq = irq;
+ gpio_keys[n_buttons].gpio = -ENOENT;
+ } else {
+ gpio_keys[n_buttons].gpio = gpio;
+ }
+
gpio_keys[n_buttons].type = info->event_type;
gpio_keys[n_buttons].code = info->event_code;
- gpio_keys[n_buttons].gpio = gpio;
- gpio_keys[n_buttons].active_low = 1;
+ gpio_keys[n_buttons].active_low = info->active_low;
gpio_keys[n_buttons].desc = info->name;
gpio_keys[n_buttons].wakeup = info->wakeup;
/* These devices often use cheap buttons, use 50 ms debounce */
@@ -114,25 +233,19 @@ soc_button_device_create(struct platform_device *pdev,
gpio_keys_pdata->nbuttons = n_buttons;
gpio_keys_pdata->rep = autorepeat;
- pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
- if (!pd) {
- error = -ENOMEM;
+ pd = platform_device_register_resndata(&pdev->dev, "gpio-keys",
+ PLATFORM_DEVID_AUTO, NULL, 0,
+ gpio_keys_pdata,
+ sizeof(*gpio_keys_pdata));
+ error = PTR_ERR_OR_ZERO(pd);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed registering gpio-keys: %d\n", error);
goto err_free_mem;
}
- error = platform_device_add_data(pd, gpio_keys_pdata,
- sizeof(*gpio_keys_pdata));
- if (error)
- goto err_free_pdev;
-
- error = platform_device_add(pd);
- if (error)
- goto err_free_pdev;
-
return pd;
-err_free_pdev:
- platform_device_put(pd);
err_free_mem:
devm_kfree(&pdev->dev, gpio_keys_pdata);
return ERR_PTR(error);
@@ -166,6 +279,7 @@ static int soc_button_parse_btn_desc(struct device *dev,
}
info->event_type = EV_KEY;
+ info->active_low = true;
info->acpi_index =
soc_button_get_acpi_object_int(&desc->package.elements[1]);
upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
@@ -185,6 +299,15 @@ static int soc_button_parse_btn_desc(struct device *dev,
info->name = "power";
info->event_code = KEY_POWER;
info->wakeup = true;
+ } else if (upage == 0x01 && usage == 0xc6) {
+ info->name = "airplane mode switch";
+ info->event_type = EV_SW;
+ info->event_code = SW_RFKILL_ALL;
+ info->active_low = false;
+ } else if (upage == 0x01 && usage == 0xca) {
+ info->name = "rotation lock switch";
+ info->event_type = EV_SW;
+ info->event_code = SW_ROTATE_LOCK;
} else if (upage == 0x07 && usage == 0xe3) {
info->name = "home";
info->event_code = KEY_LEFTMETA;
@@ -293,7 +416,7 @@ out:
return button_info;
}
-static int soc_button_remove(struct platform_device *pdev)
+static void soc_button_remove(struct platform_device *pdev)
{
struct soc_button_data *priv = platform_get_drvdata(pdev);
@@ -302,30 +425,31 @@ static int soc_button_remove(struct platform_device *pdev)
for (i = 0; i < BUTTON_TYPES; i++)
if (priv->children[i])
platform_device_unregister(priv->children[i]);
-
- return 0;
}
static int soc_button_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct acpi_device_id *id;
- struct soc_button_info *button_info;
+ const struct soc_device_data *device_data;
+ const struct soc_button_info *button_info;
struct soc_button_data *priv;
struct platform_device *pd;
int i;
int error;
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return -ENODEV;
+ device_data = acpi_device_get_match_data(dev);
+ if (device_data && device_data->check) {
+ error = device_data->check(dev);
+ if (error)
+ return error;
+ }
- if (!id->driver_data) {
+ if (device_data && device_data->button_info) {
+ button_info = device_data->button_info;
+ } else {
button_info = soc_button_get_button_info(dev);
if (IS_ERR(button_info))
return PTR_ERR(button_info);
- } else {
- button_info = (struct soc_button_info *)id->driver_data;
}
error = gpiod_count(dev, NULL);
@@ -357,7 +481,7 @@ static int soc_button_probe(struct platform_device *pdev)
if (!priv->children[0] && !priv->children[1])
return -ENODEV;
- if (!id->driver_data)
+ if (!device_data || !device_data->button_info)
devm_kfree(dev, button_info);
return 0;
@@ -368,18 +492,119 @@ static int soc_button_probe(struct platform_device *pdev)
* is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
* Platforms"
*/
-static struct soc_button_info soc_button_PNP0C40[] = {
- { "power", 0, EV_KEY, KEY_POWER, false, true },
- { "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
- { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
- { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
- { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+static const struct soc_button_info soc_button_PNP0C40[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true, true },
+ { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
+ { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
+ { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true },
+ { }
+};
+
+static const struct soc_device_data soc_device_PNP0C40 = {
+ .button_info = soc_button_PNP0C40,
+};
+
+static const struct soc_button_info soc_button_INT33D3[] = {
+ { "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false },
+ { }
+};
+
+static const struct soc_device_data soc_device_INT33D3 = {
+ .button_info = soc_button_INT33D3,
+};
+
+/*
+ * Button info for Microsoft Surface 3 (non pro), this is identical to
+ * the PNP0C40 info except that the home button is active-high.
+ *
+ * The Surface 3 Pro also has a MSHW0028 ACPI device, but that uses a custom
+ * version of the drivers/platform/x86/intel/hid.c 5 button array ACPI API
+ * instead. A check() callback is not necessary though as the Surface 3 Pro
+ * MSHW0028 ACPI device's resource table does not contain any GPIOs.
+ */
+static const struct soc_button_info soc_button_MSHW0028[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true, true },
+ { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, false },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
+ { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
+ { }
+};
+
+static const struct soc_device_data soc_device_MSHW0028 = {
+ .button_info = soc_button_MSHW0028,
+};
+
+/*
+ * Special device check for Surface Book 2 and Surface Pro (2017).
+ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
+ * devices use MSHW0040 for power and volume buttons, however the way they
+ * have to be addressed differs. Make sure that we only load this drivers
+ * for the correct devices by checking the OEM Platform Revision provided by
+ * the _DSM method.
+ */
+#define MSHW0040_DSM_REVISION 0x01
+#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision
+static const guid_t MSHW0040_DSM_UUID =
+ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65,
+ 0x49, 0x80, 0x35);
+
+static int soc_device_check_MSHW0040(struct device *dev)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object *result;
+ u64 oem_platform_rev = 0; // valid revisions are nonzero
+
+ // get OEM platform revision
+ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+ MSHW0040_DSM_REVISION,
+ MSHW0040_DSM_GET_OMPR, NULL,
+ ACPI_TYPE_INTEGER);
+
+ if (result) {
+ oem_platform_rev = result->integer.value;
+ ACPI_FREE(result);
+ }
+
+ /*
+ * If the revision is zero here, the _DSM evaluation has failed. This
+ * indicates that we have a Pro 4 or Book 1 and this driver should not
+ * be used.
+ */
+ if (oem_platform_rev == 0)
+ return -ENODEV;
+
+ dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev);
+
+ return 0;
+}
+
+/*
+ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017).
+ * Obtained from DSDT/testing.
+ */
+static const struct soc_button_info soc_button_MSHW0040[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true, true },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
+ { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
{ }
};
+static const struct soc_device_data soc_device_MSHW0040 = {
+ .button_info = soc_button_MSHW0040,
+ .check = soc_device_check_MSHW0040,
+};
+
static const struct acpi_device_id soc_button_acpi_match[] = {
- { "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+ { "PNP0C40", (unsigned long)&soc_device_PNP0C40 },
+ { "INT33D3", (unsigned long)&soc_device_INT33D3 },
+ { "ID9001", (unsigned long)&soc_device_INT33D3 },
{ "ACPI0011", 0 },
+
+ /* Microsoft Surface Devices (3th, 5th and 6th generation) */
+ { "MSHW0028", (unsigned long)&soc_device_MSHW0028 },
+ { "MSHW0040", (unsigned long)&soc_device_MSHW0040 },
+
{ }
};
@@ -395,4 +620,5 @@ static struct platform_driver soc_button_driver = {
};
module_platform_driver(soc_button_driver);
+MODULE_DESCRIPTION("Windows-compatible SoC Button Array driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index 4a5afc7fe96e..1cfadd73829f 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for PC-speaker like devices found on various Sparc systems.
*
@@ -8,7 +9,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
@@ -67,15 +69,19 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int
struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
struct bbc_beep_info *info = &state->u.bbc;
unsigned int count = 0;
- unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
- case SND_BELL: if (value) value = 1000;
- case SND_TONE: break;
- default: return -1;
+ case SND_BELL:
+ if (value)
+ value = 1000;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
}
if (value > 20 && value < 32767)
@@ -83,7 +89,7 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int
count = bbc_count_to_reg(info, count);
- spin_lock_irqsave(&state->lock, flags);
+ guard(spinlock_irqsave)(&state->lock);
if (count) {
sbus_writeb(0x01, info->regs + 0);
@@ -95,8 +101,6 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int
sbus_writeb(0x00, info->regs + 0);
}
- spin_unlock_irqrestore(&state->lock, flags);
-
return 0;
}
@@ -105,21 +109,25 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned
struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
struct grover_beep_info *info = &state->u.grover;
unsigned int count = 0;
- unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
- case SND_BELL: if (value) value = 1000;
- case SND_TONE: break;
- default: return -1;
+ case SND_BELL:
+ if (value)
+ value = 1000;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
- spin_lock_irqsave(&state->lock, flags);
+ guard(spinlock_irqsave)(&state->lock);
if (count) {
/* enable counter 2 */
@@ -134,8 +142,6 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned
sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg);
}
- spin_unlock_irqrestore(&state->lock, flags);
-
return 0;
}
@@ -186,49 +192,41 @@ static int bbc_beep_probe(struct platform_device *op)
{
struct sparcspkr_state *state;
struct bbc_beep_info *info;
- struct device_node *dp;
- int err = -ENOMEM;
+ int err;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
+ state = devm_kzalloc(&op->dev, sizeof(*state), GFP_KERNEL);
if (!state)
- goto out_err;
+ return -ENOMEM;
state->name = "Sparc BBC Speaker";
state->event = bbc_spkr_event;
spin_lock_init(&state->lock);
- dp = of_find_node_by_path("/");
- err = -ENODEV;
+ struct device_node *dp __free(device_node) = of_find_node_by_path("/");
if (!dp)
- goto out_free;
+ return -ENODEV;
info = &state->u.bbc;
info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
if (!info->clock_freq)
- goto out_free;
+ return -ENODEV;
info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
if (!info->regs)
- goto out_free;
+ return -ENODEV;
platform_set_drvdata(op, state);
err = sparcspkr_probe(&op->dev);
- if (err)
- goto out_clear_drvdata;
+ if (err) {
+ of_iounmap(&op->resource[0], info->regs, 6);
+ return err;
+ }
return 0;
-
-out_clear_drvdata:
- of_iounmap(&op->resource[0], info->regs, 6);
-
-out_free:
- kfree(state);
-out_err:
- return err;
}
-static int bbc_remove(struct platform_device *op)
+static void bbc_remove(struct platform_device *op)
{
struct sparcspkr_state *state = platform_get_drvdata(op);
struct input_dev *input_dev = state->input_dev;
@@ -240,10 +238,6 @@ static int bbc_remove(struct platform_device *op)
input_unregister_device(input_dev);
of_iounmap(&op->resource[0], info->regs, 6);
-
- kfree(state);
-
- return 0;
}
static const struct of_device_id bbc_beep_match[] = {
@@ -271,9 +265,9 @@ static int grover_beep_probe(struct platform_device *op)
struct grover_beep_info *info;
int err = -ENOMEM;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
+ state = devm_kzalloc(&op->dev, sizeof(*state), GFP_KERNEL);
if (!state)
- goto out_err;
+ return err;
state->name = "Sparc Grover Speaker";
state->event = grover_spkr_event;
@@ -282,7 +276,7 @@ static int grover_beep_probe(struct platform_device *op)
info = &state->u.grover;
info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
if (!info->freq_regs)
- goto out_free;
+ return err;
info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
if (!info->enable_reg)
@@ -301,13 +295,11 @@ out_clear_drvdata:
out_unmap_freq_regs:
of_iounmap(&op->resource[2], info->freq_regs, 2);
-out_free:
- kfree(state);
-out_err:
+
return err;
}
-static int grover_remove(struct platform_device *op)
+static void grover_remove(struct platform_device *op)
{
struct sparcspkr_state *state = platform_get_drvdata(op);
struct grover_beep_info *info = &state->u.grover;
@@ -320,10 +312,6 @@ static int grover_remove(struct platform_device *op)
of_iounmap(&op->resource[3], info->enable_reg, 1);
of_iounmap(&op->resource[2], info->freq_regs, 2);
-
- kfree(state);
-
- return 0;
}
static const struct of_device_id grover_beep_match[] = {
diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c
new file mode 100644
index 000000000000..d5ebca7b90a5
--- /dev/null
+++ b/drivers/input/misc/stpmic1_onkey.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+/**
+ * struct stpmic1_onkey - OnKey data
+ * @input_dev: pointer to input device
+ * @irq_falling: irq that we are hooked on to
+ * @irq_rising: irq that we are hooked on to
+ */
+struct stpmic1_onkey {
+ struct input_dev *input_dev;
+ int irq_falling;
+ int irq_rising;
+};
+
+static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 1);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 0);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int stpmic1_onkey_probe(struct platform_device *pdev)
+{
+ struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct stpmic1_onkey *onkey;
+ unsigned int val, reg = 0;
+ int error;
+
+ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
+ if (onkey->irq_falling < 0)
+ return onkey->irq_falling;
+
+ onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising");
+ if (onkey->irq_rising < 0)
+ return onkey->irq_rising;
+
+ if (!device_property_read_u32(dev, "power-off-time-sec", &val)) {
+ if (val > 0 && val <= 16) {
+ dev_dbg(dev, "power-off-time=%d seconds\n", val);
+ reg |= PONKEY_PWR_OFF;
+ reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK);
+ } else {
+ dev_err(dev, "power-off-time-sec out of range\n");
+ return -EINVAL;
+ }
+ }
+
+ if (device_property_present(dev, "st,onkey-clear-cc-flag"))
+ reg |= PONKEY_CC_FLAG_CLEAR;
+
+ error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
+ PONKEY_TURNOFF_MASK, reg);
+ if (error) {
+ dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error);
+ return error;
+ }
+
+ if (device_property_present(dev, "st,onkey-pu-inactive")) {
+ error = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
+ PONKEY_PU_INACTIVE,
+ PONKEY_PU_INACTIVE);
+ if (error) {
+ dev_err(dev, "ONKEY Pads configuration failed: %d\n",
+ error);
+ return error;
+ }
+ }
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "pmic_onkey";
+ input_dev->phys = "pmic_onkey/input0";
+
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+ onkey->input_dev = input_dev;
+
+ /* interrupt is nested in a thread */
+ error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
+ onkey_falling_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
+ onkey_rising_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Can't register power button: %d\n", error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ device_init_wakeup(dev, true);
+
+ return 0;
+}
+
+static int stpmic1_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(onkey->irq_falling);
+ enable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static int stpmic1_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(onkey->irq_falling);
+ disable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm,
+ stpmic1_onkey_suspend,
+ stpmic1_onkey_resume);
+
+static const struct of_device_id of_stpmic1_onkey_match[] = {
+ { .compatible = "st,stpmic1-onkey" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match);
+
+static struct platform_driver stpmic1_onkey_driver = {
+ .probe = stpmic1_onkey_probe,
+ .driver = {
+ .name = "stpmic1_onkey",
+ .of_match_table = of_match_ptr(of_stpmic1_onkey_match),
+ .pm = pm_sleep_ptr(&stpmic1_onkey_pm),
+ },
+};
+module_platform_driver(stpmic1_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for STPMIC1");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c
index a4455bb12ae0..fc450fce0932 100644
--- a/drivers/input/misc/tps65218-pwrbutton.c
+++ b/drivers/input/misc/tps65218-pwrbutton.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
* Author: Felipe Balbi <balbi@ti.com>
* Author: Marcin Niestroj <m.niestroj@grinn-global.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/init.h>
@@ -124,10 +116,8 @@ static int tps6521x_pb_probe(struct platform_device *pdev)
device_init_wakeup(dev, true);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "No IRQ resource!\n");
+ if (irq < 0)
return -EINVAL;
- }
error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq,
IRQF_TRIGGER_RISING |
diff --git a/drivers/input/misc/tps65219-pwrbutton.c b/drivers/input/misc/tps65219-pwrbutton.c
new file mode 100644
index 000000000000..7a58bae4f1a0
--- /dev/null
+++ b/drivers/input/misc/tps65219-pwrbutton.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for TPS65219 Push Button
+//
+// Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps65219.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct tps65219_pwrbutton {
+ struct device *dev;
+ struct input_dev *idev;
+ char phys[32];
+};
+
+static irqreturn_t tps65219_pb_push_irq(int irq, void *_pwr)
+{
+ struct tps65219_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 tps65219_pb_release_irq(int irq, void *_pwr)
+{
+ struct tps65219_pwrbutton *pwr = _pwr;
+
+ input_report_key(pwr->idev, KEY_POWER, 0);
+ input_sync(pwr->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int tps65219_pb_probe(struct platform_device *pdev)
+{
+ struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct tps65219_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,
+ tps65219_pb_push_irq,
+ IRQF_ONESHOT,
+ dev->init_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,
+ tps65219_pb_release_irq,
+ IRQF_ONESHOT,
+ dev->init_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;
+ }
+
+ /* Enable interrupts for the pushbutton */
+ regmap_clear_bits(tps->regmap, TPS65219_REG_MASK_CONFIG,
+ TPS65219_REG_MASK_INT_FOR_PB_MASK);
+
+ /* Set PB/EN/VSENSE pin to be a pushbutton */
+ regmap_update_bits(tps->regmap, TPS65219_REG_MFP_2_CONFIG,
+ TPS65219_MFP_2_EN_PB_VSENSE_MASK, TPS65219_MFP_2_PB);
+
+ return 0;
+}
+
+static void tps65219_pb_remove(struct platform_device *pdev)
+{
+ struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ /* Disable interrupt for the pushbutton */
+ ret = regmap_set_bits(tps->regmap, TPS65219_REG_MASK_CONFIG,
+ TPS65219_REG_MASK_INT_FOR_PB_MASK);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to disable irq (%pe)\n", ERR_PTR(ret));
+}
+
+static const struct platform_device_id tps65219_pwrbtn_id_table[] = {
+ { "tps65219-pwrbutton", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65219_pwrbtn_id_table);
+
+static struct platform_driver tps65219_pb_driver = {
+ .probe = tps65219_pb_probe,
+ .remove = tps65219_pb_remove,
+ .driver = {
+ .name = "tps65219_pwrbutton",
+ },
+ .id_table = tps65219_pwrbtn_id_table,
+};
+module_platform_driver(tps65219_pb_driver);
+
+MODULE_DESCRIPTION("TPS65219 Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com");
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/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index b307cca17022..f85cc289c053 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -1,5 +1,5 @@
-/**
- * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
+/*
+ * TWL4030 Power Button Input Driver
*
* Copyright (C) 2008-2009 Nokia Corporation
*
@@ -26,6 +26,7 @@
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/mfd/twl.h>
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index c37aea9ac272..5fa7d4a7da36 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* twl4030-vibra.c - TWL4030 Vibrator driver
*
@@ -6,21 +7,6 @@
* Written by Henrik Saari <henrik.saari@nokia.com>
* Updates by Felipe Balbi <felipe.balbi@nokia.com>
* Input by Jari Vanhala <ext-jari.vanhala@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/module.h>
@@ -157,7 +143,7 @@ static void twl4030_vibra_close(struct input_dev *input)
}
/*** Module ***/
-static int __maybe_unused twl4030_vibra_suspend(struct device *dev)
+static int twl4030_vibra_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct vibra_info *info = platform_get_drvdata(pdev);
@@ -168,41 +154,31 @@ static int __maybe_unused twl4030_vibra_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused twl4030_vibra_resume(struct device *dev)
+static int twl4030_vibra_resume(struct device *dev)
{
vibra_disable_leds();
return 0;
}
-static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
- twl4030_vibra_suspend, twl4030_vibra_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
+ twl4030_vibra_suspend, twl4030_vibra_resume);
-static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
- struct device_node *parent)
+static bool twl4030_vibra_check_coexist(struct device_node *parent)
{
- struct device_node *node;
-
- if (pdata && pdata->coexist)
- return true;
-
- node = of_get_child_by_name(parent, "codec");
- if (node) {
- of_node_put(node);
- return true;
- }
+ struct device_node *node __free(device_node) =
+ of_get_child_by_name(parent, "codec");
- return false;
+ return node != NULL;
}
static int twl4030_vibra_probe(struct platform_device *pdev)
{
- struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
struct vibra_info *info;
int ret;
- if (!pdata && !twl4030_core_node) {
- dev_dbg(&pdev->dev, "platform_data not available\n");
+ if (!twl4030_core_node) {
+ dev_dbg(&pdev->dev, "twl4030 OF node is missing\n");
return -EINVAL;
}
@@ -211,7 +187,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
return -ENOMEM;
info->dev = &pdev->dev;
- info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
+ info->coexist = twl4030_vibra_check_coexist(twl4030_core_node);
INIT_WORK(&info->play_work, vibra_play_work);
info->input_dev = devm_input_allocate_device(&pdev->dev);
@@ -253,7 +229,7 @@ static struct platform_driver twl4030_vibra_driver = {
.probe = twl4030_vibra_probe,
.driver = {
.name = "twl4030-vibra",
- .pm = &twl4030_vibra_pm_ops,
+ .pm = pm_sleep_ptr(&twl4030_vibra_pm_ops),
},
};
module_platform_driver(twl4030_vibra_driver);
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
index 15e0d352c4cc..afed9af65bf9 100644
--- a/drivers/input/misc/twl6040-vibra.c
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* twl6040-vibra.c - TWL6040 Vibrator driver
*
@@ -9,21 +10,6 @@
* Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
* Felipe Balbi <felipe.balbi@nokia.com>
* Jari Vanhala <ext-javi.vanhala@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -224,7 +210,7 @@ static void twl6040_vibra_close(struct input_dev *input)
twl6040_vibra_disable(info);
}
-static int __maybe_unused twl6040_vibra_suspend(struct device *dev)
+static int twl6040_vibra_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct vibra_info *info = platform_get_drvdata(pdev);
@@ -237,19 +223,19 @@ static int __maybe_unused twl6040_vibra_suspend(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
+static DEFINE_SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops,
+ twl6040_vibra_suspend, NULL);
static int twl6040_vibra_probe(struct platform_device *pdev)
{
struct device *twl6040_core_dev = pdev->dev.parent;
- struct device_node *twl6040_core_node;
struct vibra_info *info;
int vddvibl_uV = 0;
int vddvibr_uV = 0;
int error;
- twl6040_core_node = of_get_child_by_name(twl6040_core_dev->of_node,
- "vibra");
+ struct device_node *twl6040_core_node __free(device_node) =
+ of_get_child_by_name(twl6040_core_dev->of_node, "vibra");
if (!twl6040_core_node) {
dev_err(&pdev->dev, "parent of node is missing?\n");
return -EINVAL;
@@ -257,7 +243,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
- of_node_put(twl6040_core_node);
dev_err(&pdev->dev, "couldn't allocate memory\n");
return -ENOMEM;
}
@@ -277,8 +262,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
of_property_read_u32(twl6040_core_node, "ti,vddvibl-uV", &vddvibl_uV);
of_property_read_u32(twl6040_core_node, "ti,vddvibr-uV", &vddvibr_uV);
- of_node_put(twl6040_core_node);
-
if ((!info->vibldrv_res && !info->viblmotor_res) ||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
@@ -286,10 +269,8 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
}
info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- dev_err(info->dev, "invalid irq\n");
+ if (info->irq < 0)
return -EINVAL;
- }
error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
twl6040_vib_irq_handler,
@@ -370,7 +351,7 @@ static struct platform_driver twl6040_vibra_driver = {
.probe = twl6040_vibra_probe,
.driver = {
.name = "twl6040-vibra",
- .pm = &twl6040_vibra_pm_ops,
+ .pm = pm_sleep_ptr(&twl6040_vibra_pm_ops),
},
};
module_platform_driver(twl6040_vibra_driver);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 8ec483e8688b..13336a2fd49c 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* User level driver support for input subsystem
*
* Heavily based on evdev.c by Vojtech Pavlik
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
@@ -39,12 +26,14 @@
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
+#include <linux/overflow.h>
#include <linux/input/mt.h>
#include "../input-compat.h"
#define UINPUT_NAME "uinput"
#define UINPUT_BUFFER_SIZE 16
#define UINPUT_NUM_REQUESTS 16
+#define UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS 10
enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED };
@@ -86,12 +75,16 @@ static int uinput_dev_event(struct input_dev *dev,
struct uinput_device *udev = input_get_drvdata(dev);
struct timespec64 ts;
- udev->buff[udev->head].type = type;
- udev->buff[udev->head].code = code;
- udev->buff[udev->head].value = value;
ktime_get_ts64(&ts);
- udev->buff[udev->head].input_event_sec = ts.tv_sec;
- udev->buff[udev->head].input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+ udev->buff[udev->head] = (struct input_event) {
+ .input_event_sec = ts.tv_sec,
+ .input_event_usec = ts.tv_nsec / NSEC_PER_USEC,
+ .type = type,
+ .code = code,
+ .value = value,
+ };
+
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq);
@@ -386,7 +379,7 @@ static int uinput_open(struct inode *inode, struct file *file)
{
struct uinput_device *newdev;
- newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
+ newdev = kzalloc(sizeof(*newdev), GFP_KERNEL);
if (!newdev)
return -ENOMEM;
@@ -397,7 +390,7 @@ static int uinput_open(struct inode *inode, struct file *file)
newdev->state = UIST_NEW_DEVICE;
file->private_data = newdev;
- nonseekable_open(inode, file);
+ stream_open(inode, file);
return 0;
}
@@ -405,7 +398,7 @@ static int uinput_open(struct inode *inode, struct file *file)
static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
const struct input_absinfo *abs)
{
- int min, max;
+ int min, max, range;
min = abs->minimum;
max = abs->maximum;
@@ -417,13 +410,27 @@ static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
return -EINVAL;
}
- if (abs->flat > max - min) {
+ if (!check_sub_overflow(max, min, &range) && abs->flat > range) {
printk(KERN_DEBUG
"%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
UINPUT_NAME, code, abs->flat, min, max);
return -EINVAL;
}
+ /*
+ * Limit number of contacts to a reasonable value (100). This
+ * ensures that we need less than 2 pages for struct input_mt
+ * (we are not using in-kernel slot assignment so not going to
+ * allocate memory for the "red" table), and we should have no
+ * trouble getting this much memory.
+ */
+ if (code == ABS_MT_SLOT && max > 99) {
+ printk(KERN_DEBUG
+ "%s: unreasonably large number of slots requested: %d\n",
+ UINPUT_NAME, max);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -577,11 +584,40 @@ static int uinput_setup_device_legacy(struct uinput_device *udev,
return retval;
}
+/*
+ * Returns true if the given timestamp is valid (i.e., if all the following
+ * conditions are satisfied), false otherwise.
+ * 1) given timestamp is positive
+ * 2) it's within the allowed offset before the current time
+ * 3) it's not in the future
+ */
+static bool is_valid_timestamp(const ktime_t timestamp)
+{
+ ktime_t zero_time;
+ ktime_t current_time;
+ ktime_t min_time;
+ ktime_t offset;
+
+ zero_time = ktime_set(0, 0);
+ if (ktime_compare(zero_time, timestamp) >= 0)
+ return false;
+
+ current_time = ktime_get();
+ offset = ktime_set(UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS, 0);
+ min_time = ktime_sub(current_time, offset);
+
+ if (ktime_after(min_time, timestamp) || ktime_after(timestamp, current_time))
+ return false;
+
+ return true;
+}
+
static ssize_t uinput_inject_events(struct uinput_device *udev,
const char __user *buffer, size_t count)
{
struct input_event ev;
size_t bytes = 0;
+ ktime_t timestamp;
if (count != 0 && count < input_event_size())
return -EINVAL;
@@ -596,6 +632,10 @@ static ssize_t uinput_inject_events(struct uinput_device *udev,
if (input_event_from_user(buffer + bytes, &ev))
return -EFAULT;
+ timestamp = ktime_set(ev.input_event_sec, ev.input_event_usec * NSEC_PER_USEC);
+ if (is_valid_timestamp(timestamp))
+ input_set_timestamp(udev->dev, timestamp);
+
input_event(udev->dev, ev.type, ev.code, ev.value);
bytes += input_event_size();
cond_resched();
@@ -701,13 +741,14 @@ static ssize_t uinput_read(struct file *file, char __user *buffer,
static __poll_t uinput_poll(struct file *file, poll_table *wait)
{
struct uinput_device *udev = file->private_data;
+ __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uinput is always writable */
poll_wait(file, &udev->waitq, wait);
if (udev->head != udev->tail)
- return EPOLLIN | EPOLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
- return 0;
+ return mask;
}
static int uinput_release(struct inode *inode, struct file *file)
@@ -734,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;
/*
@@ -1050,13 +1092,31 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#ifdef CONFIG_COMPAT
-#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
+/*
+ * These IOCTLs change their size and thus their numbers between
+ * 32 and 64 bits.
+ */
+#define UI_SET_PHYS_COMPAT \
+ _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
+#define UI_BEGIN_FF_UPLOAD_COMPAT \
+ _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload_compat)
+#define UI_END_FF_UPLOAD_COMPAT \
+ _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload_compat)
static long uinput_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
- if (cmd == UI_SET_PHYS_COMPAT)
+ switch (cmd) {
+ case UI_SET_PHYS_COMPAT:
cmd = UI_SET_PHYS;
+ break;
+ case UI_BEGIN_FF_UPLOAD_COMPAT:
+ cmd = UI_BEGIN_FF_UPLOAD;
+ break;
+ case UI_END_FF_UPLOAD_COMPAT:
+ cmd = UI_END_FF_UPLOAD;
+ break;
+ }
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
}
@@ -1073,7 +1133,6 @@ static const struct file_operations uinput_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl = uinput_compat_ioctl,
#endif
- .llseek = no_llseek,
};
static struct miscdevice uinput_misc = {
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index 43e67f546366..1b2bd2139aaa 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -1,26 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Wistron laptop button driver
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
* Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
- *
- * You can redistribute and/or modify this program under the terms of the
- * GNU General Public License version 2 as published by the Free Software
- * Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/io.h>
#include <linux/dmi.h>
#include <linux/init.h>
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@@ -283,7 +271,7 @@ static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = {
{ KE_BLUETOOTH, 0x30 }, /* Fn+F10 */
{ KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */
{ KE_KEY, 0x36, {KEY_WWW} }, /* www button */
- { KE_WIFI, 0x78 }, /* satelite dish button */
+ { KE_WIFI, 0x78 }, /* satellite dish button */
{ KE_END, FE_WIFI_LED }
};
@@ -1002,8 +990,8 @@ static int __init copy_keymap(void)
for (key = keymap; key->type != KE_END; key++)
length++;
- new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),
- GFP_KERNEL);
+ new_keymap = kmemdup_array(keymap, length, sizeof(struct key_entry),
+ GFP_KERNEL);
if (!new_keymap)
return -ENOMEM;
@@ -1042,7 +1030,7 @@ static int __init select_keymap(void)
/* Input layer interface */
-static struct input_polled_dev *wistron_idev;
+static struct input_dev *wistron_idev;
static unsigned long jiffies_last_press;
static bool wifi_enabled;
static bool bluetooth_enabled;
@@ -1087,7 +1075,7 @@ static void wistron_led_init(struct device *parent)
}
if (leds_present & FE_MAIL_LED) {
- /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */
+ /* bios_get_default_setting(MAIL) always returns 0, so just turn the led off */
wistron_mail_led.brightness = LED_OFF;
if (led_classdev_register(parent, &wistron_mail_led))
leds_present &= ~FE_MAIL_LED;
@@ -1126,7 +1114,7 @@ static inline void wistron_led_resume(void)
static void handle_key(u8 code)
{
const struct key_entry *key =
- sparse_keymap_entry_from_scancode(wistron_idev->input, code);
+ sparse_keymap_entry_from_scancode(wistron_idev, code);
if (key) {
switch (key->type) {
@@ -1145,14 +1133,14 @@ static void handle_key(u8 code)
break;
default:
- sparse_keymap_report_entry(wistron_idev->input,
- key, 1, true);
+ sparse_keymap_report_entry(wistron_idev, key, 1, true);
break;
}
jiffies_last_press = jiffies;
- } else
+ } else {
printk(KERN_NOTICE
"wistron_btns: Unknown key code %02X\n", code);
+ }
}
static void poll_bios(bool discard)
@@ -1170,21 +1158,23 @@ static void poll_bios(bool discard)
}
}
-static void wistron_flush(struct input_polled_dev *dev)
+static int wistron_flush(struct input_dev *dev)
{
/* Flush stale event queue */
poll_bios(true);
+
+ return 0;
}
-static void wistron_poll(struct input_polled_dev *dev)
+static void wistron_poll(struct input_dev *dev)
{
poll_bios(false);
/* Increase poll frequency if user is currently pressing keys (< 2s ago) */
if (time_before(jiffies, jiffies_last_press + 2 * HZ))
- dev->poll_interval = POLL_INTERVAL_BURST;
+ input_set_poll_interval(dev, POLL_INTERVAL_BURST);
else
- dev->poll_interval = POLL_INTERVAL_DEFAULT;
+ input_set_poll_interval(dev, POLL_INTERVAL_DEFAULT);
}
static int wistron_setup_keymap(struct input_dev *dev,
@@ -1220,35 +1210,37 @@ static int wistron_setup_keymap(struct input_dev *dev,
static int setup_input_dev(void)
{
- struct input_dev *input_dev;
int error;
- wistron_idev = input_allocate_polled_device();
+ wistron_idev = input_allocate_device();
if (!wistron_idev)
return -ENOMEM;
+ wistron_idev->name = "Wistron laptop buttons";
+ wistron_idev->phys = "wistron/input0";
+ wistron_idev->id.bustype = BUS_HOST;
+ wistron_idev->dev.parent = &wistron_device->dev;
+
wistron_idev->open = wistron_flush;
- wistron_idev->poll = wistron_poll;
- wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
- input_dev = wistron_idev->input;
- input_dev->name = "Wistron laptop buttons";
- input_dev->phys = "wistron/input0";
- input_dev->id.bustype = BUS_HOST;
- input_dev->dev.parent = &wistron_device->dev;
+ error = sparse_keymap_setup(wistron_idev, keymap, wistron_setup_keymap);
+ if (error)
+ goto err_free_dev;
- error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);
+ error = input_setup_polling(wistron_idev, wistron_poll);
if (error)
goto err_free_dev;
- error = input_register_polled_device(wistron_idev);
+ input_set_poll_interval(wistron_idev, POLL_INTERVAL_DEFAULT);
+
+ error = input_register_device(wistron_idev);
if (error)
goto err_free_dev;
return 0;
err_free_dev:
- input_free_polled_device(wistron_idev);
+ input_free_device(wistron_idev);
return error;
}
@@ -1294,17 +1286,13 @@ static int wistron_probe(struct platform_device *dev)
return 0;
}
-static int wistron_remove(struct platform_device *dev)
+static void wistron_remove(struct platform_device *dev)
{
wistron_led_remove();
- input_unregister_polled_device(wistron_idev);
- input_free_polled_device(wistron_idev);
+ input_unregister_device(wistron_idev);
bios_detach();
-
- return 0;
}
-#ifdef CONFIG_PM
static int wistron_suspend(struct device *dev)
{
if (have_wifi)
@@ -1339,14 +1327,11 @@ static const struct dev_pm_ops wistron_pm_ops = {
.poweroff = wistron_suspend,
.restore = wistron_resume,
};
-#endif
static struct platform_driver wistron_driver = {
.driver = {
.name = "wistron-bios",
-#ifdef CONFIG_PM
- .pm = &wistron_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&wistron_pm_ops),
},
.probe = wistron_probe,
.remove = wistron_remove,
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
index 1b44de265a0a..18eb319bc6f7 100644
--- a/drivers/input/misc/wm831x-on.c
+++ b/drivers/input/misc/wm831x-on.c
@@ -1,4 +1,4 @@
-/**
+/*
* wm831x-on.c - WM831X ON pin driver
*
* Copyright (C) 2009 Wolfson Microelectronics plc
@@ -123,15 +123,13 @@ err:
return ret;
}
-static int wm831x_on_remove(struct platform_device *pdev)
+static void wm831x_on_remove(struct platform_device *pdev)
{
struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
free_irq(irq, wm831x_on);
cancel_delayed_work_sync(&wm831x_on->work);
-
- return 0;
}
static struct platform_driver wm831x_on_driver = {
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 24bc5c5d876f..67f1c7364c95 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -51,7 +51,7 @@ module_param_array(ptr_size, int, NULL, 0444);
MODULE_PARM_DESC(ptr_size,
"Pointing device width, height in pixels (default 800,600)");
-static int xenkbd_remove(struct xenbus_device *);
+static void xenkbd_remove(struct xenbus_device *);
static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
static void xenkbd_disconnect_backend(struct xenkbd_info *);
@@ -124,7 +124,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info,
switch (mtouch->event_type) {
case XENKBD_MT_EV_DOWN:
input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
- /* fall through */
+ fallthrough;
case XENKBD_MT_EV_MOTION:
input_report_abs(info->mtouch, ABS_MT_POSITION_X,
@@ -146,7 +146,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info,
break;
case XENKBD_MT_EV_UP:
- input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(info->mtouch);
break;
case XENKBD_MT_EV_SYN:
@@ -404,7 +404,7 @@ static int xenkbd_resume(struct xenbus_device *dev)
return xenkbd_connect_backend(dev, info);
}
-static int xenkbd_remove(struct xenbus_device *dev)
+static void xenkbd_remove(struct xenbus_device *dev)
{
struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
@@ -417,7 +417,6 @@ static int xenkbd_remove(struct xenbus_device *dev)
input_unregister_device(info->mtouch);
free_page((unsigned long)info->page);
kfree(info);
- return 0;
}
static int xenkbd_connect_backend(struct xenbus_device *dev,
@@ -481,7 +480,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
error_evtchan:
xenbus_free_evtchn(dev, evtchn);
error_grant:
- gnttab_end_foreign_access(info->gref, 0, 0UL);
+ gnttab_end_foreign_access(info->gref, NULL);
info->gref = -1;
return ret;
}
@@ -492,7 +491,7 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info)
unbind_from_irqhandler(info->irq, info);
info->irq = -1;
if (info->gref >= 0)
- gnttab_end_foreign_access(info->gref, 0, 0UL);
+ gnttab_end_foreign_access(info->gref, NULL);
info->gref = -1;
}
@@ -524,7 +523,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
case XenbusStateClosed:
if (dev->state == XenbusStateClosed)
break;
- /* fall through - Missed the backend's CLOSING state */
+ fallthrough; /* Missed the backend's CLOSING state */
case XenbusStateClosing:
xenbus_frontend_closed(dev);
break;
@@ -542,6 +541,7 @@ static struct xenbus_driver xenkbd_driver = {
.remove = xenkbd_remove,
.resume = xenkbd_resume,
.otherend_changed = xenkbd_backend_changed,
+ .not_essential = true,
};
static int __init xenkbd_init(void)
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
index 1365cd94ed9b..08dc53ae1b3c 100644
--- a/drivers/input/misc/yealink.c
+++ b/drivers/input/misc/yealink.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/usb/input/yealink.c
*
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Description:
@@ -49,7 +36,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/rwsem.h>
+#include <linux/mutex.h>
#include <linux/usb/input.h>
#include <linux/map_to_7segment.h>
@@ -116,6 +103,8 @@ struct yealink_dev {
u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
int key_code; /* last reported key */
+ struct mutex sysfs_mutex;
+
unsigned int shutdown:1;
int stat_ix;
@@ -388,7 +377,7 @@ send_update:
if (len > sizeof(yld->ctl_data->data))
len = sizeof(yld->ctl_data->data);
- /* Combine up to <len> consecutive LCD bytes in a singe request
+ /* Combine up to <len> consecutive LCD bytes in a single request
*/
yld->ctl_data->cmd = CMD_LCD;
yld->ctl_data->offset = cpu_to_be16(ix);
@@ -561,8 +550,6 @@ static void input_close(struct input_dev *dev)
* sysfs interface
******************************************************************************/
-static DECLARE_RWSEM(sysfs_rwsema);
-
/* Interface to the 7-segments translation table aka. char set.
*/
static ssize_t show_map(struct device *dev, struct device_attribute *attr,
@@ -593,15 +580,10 @@ static ssize_t store_map(struct device *dev, struct device_attribute *attr,
*/
static ssize_t show_line(struct device *dev, char *buf, int a, int b)
{
- struct yealink_dev *yld;
+ struct yealink_dev *yld = dev_get_drvdata(dev);
int i;
- down_read(&sysfs_rwsema);
- yld = dev_get_drvdata(dev);
- if (yld == NULL) {
- up_read(&sysfs_rwsema);
- return -ENODEV;
- }
+ guard(mutex)(&yld->sysfs_mutex);
for (i = a; i < b; i++)
*buf++ = lcdMap[i].type;
@@ -611,7 +593,6 @@ static ssize_t show_line(struct device *dev, char *buf, int a, int b)
*buf++ = '\n';
*buf = 0;
- up_read(&sysfs_rwsema);
return 3 + ((b - a) << 1);
}
@@ -633,7 +614,7 @@ static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
}
-/* Writing to /sys/../lineX will set the coresponding LCD line.
+/* Writing to /sys/../lineX will set the corresponding LCD line.
* - Excess characters are ignored.
* - If less characters are written than allowed, the remaining digits are
* unchanged.
@@ -643,22 +624,16 @@ static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
static ssize_t store_line(struct device *dev, const char *buf, size_t count,
int el, size_t len)
{
- struct yealink_dev *yld;
+ struct yealink_dev *yld = dev_get_drvdata(dev);
int i;
- down_write(&sysfs_rwsema);
- yld = dev_get_drvdata(dev);
- if (yld == NULL) {
- up_write(&sysfs_rwsema);
- return -ENODEV;
- }
+ guard(mutex)(&yld->sysfs_mutex);
if (len > count)
len = count;
for (i = 0; i < len; i++)
setChar(yld, el++, buf[i]);
- up_write(&sysfs_rwsema);
return count;
}
@@ -688,15 +663,10 @@ static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct yealink_dev *yld;
+ struct yealink_dev *yld = dev_get_drvdata(dev);
int i, ret = 1;
- down_read(&sysfs_rwsema);
- yld = dev_get_drvdata(dev);
- if (yld == NULL) {
- up_read(&sysfs_rwsema);
- return -ENODEV;
- }
+ guard(mutex)(&yld->sysfs_mutex);
for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
if (lcdMap[i].type != '.')
@@ -705,7 +675,7 @@ static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
yld->lcdMap[i] == ' ' ? " " : "on",
lcdMap[i].u.p.name);
}
- up_read(&sysfs_rwsema);
+
return ret;
}
@@ -713,15 +683,10 @@ static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
int chr)
{
- struct yealink_dev *yld;
+ struct yealink_dev *yld = dev_get_drvdata(dev);
int i;
- down_write(&sysfs_rwsema);
- yld = dev_get_drvdata(dev);
- if (yld == NULL) {
- up_write(&sysfs_rwsema);
- return -ENODEV;
- }
+ guard(mutex)(&yld->sysfs_mutex);
for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
if (lcdMap[i].type != '.')
@@ -732,7 +697,6 @@ static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
}
}
- up_write(&sysfs_rwsema);
return count;
}
@@ -752,22 +716,16 @@ static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
*/
/* Stores raw ringtone data in the phone */
-static ssize_t store_ringtone(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t store_ringtone(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct yealink_dev *yld;
+ struct yealink_dev *yld = dev_get_drvdata(dev);
- down_write(&sysfs_rwsema);
- yld = dev_get_drvdata(dev);
- if (yld == NULL) {
- up_write(&sysfs_rwsema);
- return -ENODEV;
- }
+ guard(mutex)(&yld->sysfs_mutex);
/* TODO locking with async usb control interface??? */
yealink_set_ringtone(yld, (char *)buf, count);
- up_write(&sysfs_rwsema);
+
return count;
}
@@ -784,7 +742,7 @@ static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );
static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );
static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);
-static struct attribute *yld_attributes[] = {
+static struct attribute *yld_attrs[] = {
&dev_attr_line1.attr,
&dev_attr_line2.attr,
&dev_attr_line3.attr,
@@ -795,10 +753,7 @@ static struct attribute *yld_attributes[] = {
&dev_attr_ringtone.attr,
NULL
};
-
-static const struct attribute_group yld_attr_group = {
- .attrs = yld_attributes
-};
+ATTRIBUTE_GROUPS(yld);
/*******************************************************************************
* Linux interface and usb initialisation
@@ -851,15 +806,10 @@ static int usb_cleanup(struct yealink_dev *yld, int err)
static void usb_disconnect(struct usb_interface *intf)
{
- struct yealink_dev *yld;
-
- down_write(&sysfs_rwsema);
- yld = usb_get_intfdata(intf);
- sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
- usb_set_intfdata(intf, NULL);
- up_write(&sysfs_rwsema);
+ struct yealink_dev *yld = usb_get_intfdata(intf);
usb_cleanup(yld, 0);
+ usb_set_intfdata(intf, NULL);
}
static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -881,12 +831,13 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
- yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL);
+ yld = kzalloc(sizeof(*yld), GFP_KERNEL);
if (!yld)
return -ENOMEM;
yld->udev = udev;
yld->intf = intf;
+ mutex_init(&yld->sysfs_mutex);
yld->idev = input_dev = input_allocate_device();
if (!input_dev)
@@ -918,7 +869,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
- ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ ret = usb_maxpacket(udev, pipe);
if (ret != USB_PKT_LEN)
dev_err(&intf->dev, "invalid payload size %d, expected %zd\n",
ret, USB_PKT_LEN);
@@ -988,8 +939,6 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
store_line3(&intf->dev, NULL,
DRIVER_VERSION, sizeof(DRIVER_VERSION));
- /* Register sysfs hooks (don't care about failure) */
- ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
return 0;
}
@@ -998,6 +947,7 @@ static struct usb_driver yealink_driver = {
.probe = usb_probe,
.disconnect = usb_disconnect,
.id_table = usb_table,
+ .dev_groups = yld_groups,
};
module_usb_driver(yealink_driver);
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
index 934c247f8a0f..69f700431f2f 100644
--- a/drivers/input/misc/yealink.h
+++ b/drivers/input/misc/yealink.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* drivers/usb/input/yealink.h
*
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef INPUT_YEALINK_H
#define INPUT_YEALINK_H