diff options
Diffstat (limited to 'drivers/input/misc')
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, ®_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", ®); + 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", ®_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 = ®ulator_haptic_pm_ops, + .pm = pm_sleep_ptr(®ulator_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 |
