diff options
Diffstat (limited to 'drivers/input/misc')
60 files changed, 5485 insertions, 1093 deletions
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c new file mode 100644 index 00000000000..ee43e5b7c88 --- /dev/null +++ b/drivers/input/misc/88pm80x_onkey.c @@ -0,0 +1,168 @@ +/* + * 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> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/mfd/88pm80x.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define PM800_LONG_ONKEY_EN (1 << 0) +#define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */ +#define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4) +#define PM800_LONKEY_PRESS_TIME_MASK (0xF0) +#define PM800_SW_PDOWN (1 << 5) + +struct pm80x_onkey_info { + struct input_dev *idev; + struct pm80x_chip *pm80x; + struct regmap *map; + int irq; +}; + +/* 88PM80x gives us an interrupt when ONKEY is held */ +static irqreturn_t pm80x_onkey_handler(int irq, void *data) +{ + struct pm80x_onkey_info *info = data; + int ret = 0; + unsigned int val; + + ret = regmap_read(info->map, PM800_STATUS_1, &val); + if (ret < 0) { + dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret); + return IRQ_NONE; + } + val &= PM800_ONKEY_STS1; + + input_report_key(info->idev, KEY_POWER, val); + input_sync(info->idev); + + return IRQ_HANDLED; +} + +static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend, + pm80x_dev_resume); + +static int pm80x_onkey_probe(struct platform_device *pdev) +{ + + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm80x_onkey_info *info; + int err; + + info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pm80x = chip; + + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + err = -EINVAL; + goto out; + } + + info->map = info->pm80x->regmap; + if (!info->map) { + dev_err(&pdev->dev, "no regmap!\n"); + err = -EINVAL; + goto out; + } + + info->idev = input_allocate_device(); + if (!info->idev) { + dev_err(&pdev->dev, "Failed to allocate input dev\n"); + err = -ENOMEM; + goto out; + } + + info->idev->name = "88pm80x_on"; + info->idev->phys = "88pm80x_on/input0"; + info->idev->id.bustype = BUS_I2C; + info->idev->dev.parent = &pdev->dev; + info->idev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, info->idev->keybit); + + err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler, + IRQF_ONESHOT, "onkey", info); + if (err < 0) { + dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, err); + goto out_reg; + } + + err = input_register_device(info->idev); + if (err) { + dev_err(&pdev->dev, "Can't register input device: %d\n", err); + goto out_irq; + } + + platform_set_drvdata(pdev, info); + + /* Enable long onkey detection */ + regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN, + PM800_LONG_ONKEY_EN); + /* Set 8-second interval */ + regmap_update_bits(info->map, PM800_RTC_MISC3, + PM800_LONKEY_PRESS_TIME_MASK, + PM800_LONKEY_PRESS_TIME); + + device_init_wakeup(&pdev->dev, 1); + return 0; + +out_irq: + pm80x_free_irq(info->pm80x, info->irq, info); +out_reg: + input_free_device(info->idev); +out: + kfree(info); + return err; +} + +static int pm80x_onkey_remove(struct platform_device *pdev) +{ + struct pm80x_onkey_info *info = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + pm80x_free_irq(info->pm80x, info->irq, info); + input_unregister_device(info->idev); + kfree(info); + return 0; +} + +static struct platform_driver pm80x_onkey_driver = { + .driver = { + .name = "88pm80x-onkey", + .owner = THIS_MODULE, + .pm = &pm80x_onkey_pm_ops, + }, + .probe = pm80x_onkey_probe, + .remove = pm80x_onkey_remove, +}; + +module_platform_driver(pm80x_onkey_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_ALIAS("platform:88pm80x-onkey"); diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c index f2e0cbc5ab6..220ce0fa15d 100644 --- a/drivers/input/misc/88pm860x_onkey.c +++ b/drivers/input/misc/88pm860x_onkey.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/mfd/88pm860x.h> #include <linux/slab.h> +#include <linux/device.h> #define PM8607_WAKEUP 0x0b @@ -56,7 +57,7 @@ static irqreturn_t pm860x_onkey_handler(int irq, void *data) return IRQ_HANDLED; } -static int __devinit pm860x_onkey_probe(struct platform_device *pdev) +static int pm860x_onkey_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm860x_onkey_info *info; @@ -68,7 +69,8 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) return -EINVAL; } - info = kzalloc(sizeof(struct pm860x_onkey_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_onkey_info), + GFP_KERNEL); if (!info) return -ENOMEM; info->chip = chip; @@ -76,11 +78,10 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->irq = irq; - info->idev = input_allocate_device(); + info->idev = devm_input_allocate_device(&pdev->dev); if (!info->idev) { dev_err(chip->dev, "Failed to allocate input dev\n"); - ret = -ENOMEM; - goto out; + return -ENOMEM; } info->idev->name = "88pm860x_on"; @@ -93,49 +94,54 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) ret = input_register_device(info->idev); if (ret) { dev_err(chip->dev, "Can't register input device: %d\n", ret); - goto out_reg; + return ret; } - ret = request_threaded_irq(info->irq, NULL, pm860x_onkey_handler, - IRQF_ONESHOT, "onkey", info); + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + pm860x_onkey_handler, IRQF_ONESHOT, + "onkey", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq, ret); - goto out_irq; + return ret; } platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + return 0; +} -out_irq: - input_unregister_device(info->idev); - kfree(info); - return ret; +#ifdef CONFIG_PM_SLEEP +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); -out_reg: - input_free_device(info->idev); -out: - kfree(info); - return ret; + if (device_may_wakeup(dev)) + chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY; + return 0; } - -static int __devexit pm860x_onkey_remove(struct platform_device *pdev) +static int pm860x_onkey_resume(struct device *dev) { - struct pm860x_onkey_info *info = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - free_irq(info->irq, info); - input_unregister_device(info->idev); - kfree(info); + if (device_may_wakeup(dev)) + chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY); return 0; } +#endif + +static 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", .owner = THIS_MODULE, + .pm = &pm860x_onkey_pm_ops, }, .probe = pm860x_onkey_probe, - .remove = __devexit_p(pm860x_onkey_remove), }; module_platform_driver(pm860x_onkey_driver); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7b46781c30c..2ff4425a893 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_88PM80X_ONKEY + tristate "88PM80x ONKEY support" + depends on MFD_88PM800 + help + Support the ONKEY of Marvell 88PM80x PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called 88pm80x_onkey. + config INPUT_AB8500_PONKEY tristate "AB8500 Pon (PowerOn) Key" depends on AB8500_CORE @@ -62,6 +72,16 @@ config INPUT_AD714X_SPI To compile this driver as a module, choose M here: the module will be called ad714x-spi. +config INPUT_ARIZONA_HAPTICS + tristate "Arizona haptics support" + depends on MFD_ARIZONA && SND_SOC + select INPUT_FF_MEMLESS + help + Say Y to enable support for the haptics module in Arizona CODECs. + + To compile this driver as a module, choose M here: the + module will be called arizona-haptics. + config INPUT_BMA150 tristate "BMA150/SMB380 acceleration sensor support" depends on I2C @@ -134,9 +154,21 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + config INPUT_MC13783_PWRBUTTON tristate "MC13783 ON buttons" - depends on MFD_MC13783 + depends on MFD_MC13XXX help Support the ON buttons of MC13783 PMIC as an input device reporting power button status. @@ -182,7 +214,7 @@ config INPUT_APANEL config INPUT_GP2A tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver" depends on I2C - depends on GENERIC_GPIO + depends on GPIOLIB help Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip hooked to an I2C bus. @@ -190,9 +222,18 @@ config INPUT_GP2A 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 + help + Say Y here if you have a beeper connected to a GPIO pin. + + To compile this driver as a module, choose M here: the + module will be called gpio-beeper. + config INPUT_GPIO_TILT_POLLED tristate "Polled GPIO tilt switch" - depends on GENERIC_GPIO + depends on GPIOLIB select INPUT_POLLDEV help This driver implements support for tilt switches connected @@ -228,7 +269,7 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" - depends on X86 && !X86_64 + depends on X86_32 select INPUT_POLLDEV select INPUT_SPARSEKMAP select NEW_LEDS @@ -268,8 +309,7 @@ config INPUT_ATI_REMOTE2 called ati_remote2. config INPUT_KEYSPAN_REMOTE - tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Keyspan DMR USB remote control" depends on USB_ARCH_HAS_HCD select USB help @@ -318,7 +358,6 @@ config INPUT_POWERMATE config INPUT_YEALINK tristate "Yealink usb-p1k voip phone" - depends on EXPERIMENTAL depends on USB_ARCH_HAS_HCD select USB help @@ -334,7 +373,6 @@ config INPUT_YEALINK config INPUT_CM109 tristate "C-Media CM109 USB I/O Controller" - depends on EXPERIMENTAL depends on USB_ARCH_HAS_HCD select USB help @@ -345,6 +383,16 @@ config INPUT_CM109 To compile this driver as a module, choose M here: the module will be called cm109. +config INPUT_RETU_PWRBUTTON + tristate "Retu Power button Driver" + depends on MFD_RETU + help + Say Y here if you want to enable power key reporting via the + Retu chips found in Nokia Internet Tablets (770, N800, N810). + + To compile this driver as a module, choose M here. The module will + be called retu-pwrbutton. + config INPUT_TWL4030_PWRBUTTON tristate "TWL4030 Power button Driver" depends on TWL4030_CORE @@ -368,8 +416,7 @@ config INPUT_TWL4030_VIBRA config INPUT_TWL6040_VIBRA tristate "Support for TWL6040 Vibrator" - depends on TWL4030_CORE - select TWL6040_CORE + depends on TWL6040_CORE select INPUT_FF_MEMLESS help This option enables support for TWL6040 Vibrator Driver. @@ -413,9 +460,9 @@ config INPUT_PCF50633_PMU config INPUT_PCF8574 tristate "PCF8574 Keypad input device" - depends on I2C && EXPERIMENTAL + depends on I2C help - Say Y here if you want to support a keypad connetced via I2C + Say Y here if you want to support a keypad connected via I2C with a PCF8574. To compile this driver as a module, choose M here: the @@ -423,7 +470,7 @@ config INPUT_PCF8574 config INPUT_PWM_BEEPER tristate "PWM beeper support" - depends on HAVE_PWM + depends on PWM help Say Y here to get support for PWM based beeper devices. @@ -434,7 +481,7 @@ config INPUT_PWM_BEEPER config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" - depends on GPIOLIB && GENERIC_GPIO + depends on GPIOLIB help Say Y here to add support for rotary encoders connected to GPIO lines. Check file:Documentation/input/rotary-encoder.txt for more @@ -446,7 +493,7 @@ config INPUT_GPIO_ROTARY_ENCODER config INPUT_RB532_BUTTON tristate "Mikrotik Routerboard 532 button interface" depends on MIKROTIK_RB532 - depends on GPIOLIB && GENERIC_GPIO + depends on GPIOLIB select INPUT_POLLDEV help Say Y here if you want support for the S1 button built into @@ -455,6 +502,26 @@ config INPUT_RB532_BUTTON To compile this driver as a module, choose M here: the module will be called rb532_button. +config INPUT_DA9052_ONKEY + tristate "Dialog DA9052/DA9053 Onkey" + depends on PMIC_DA9052 + help + Support the ONKEY of Dialog DA9052 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the + module will be called da9052_onkey. + +config INPUT_DA9055_ONKEY + tristate "Dialog Semiconductor DA9055 ONKEY" + depends on MFD_DA9055 + help + Support the ONKEY of DA9055 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called da9055_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP @@ -532,6 +599,16 @@ config INPUT_ADXL34X_SPI To compile this driver as a module, choose M here: the module will be called adxl34x-spi. +config INPUT_IMS_PCU + tristate "IMS Passenger Control Unit driver" + depends on USB + depends on LEDS_CLASS + help + Say Y here if you have system with IMS Rave Passenger Control Unit. + + To compile this driver as a module, choose M here: the module will be + called ims_pcu. + config INPUT_CMA3000 tristate "VTI CMA3000 Tri-axis accelerometer" help @@ -558,7 +635,7 @@ config INPUT_CMA3000_I2C config INPUT_XEN_KBDDEV_FRONTEND tristate "Xen virtual keyboard and mouse support" - depends on XEN_FBDEV_FRONTEND + depends on XEN default y select XEN_XENBUS_FRONTEND help @@ -569,4 +646,34 @@ 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 + bool "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 + depends on SERIO_I8042 + help + Say Y here if you have an IdeaPad laptop with a slidebar. + + To compile this driver as a module, choose M here: the + module will be called ideapad_slidebar. + +config INPUT_SOC_BUTTON_ARRAY + tristate "Windows-compatible SoC Button Array" + depends on KEYBOARD_GPIO + help + Say Y here if you have a SoC-based tablet that originally + runs Windows 8. + + To compile this driver as a module, choose M here: the + module will be called soc_button_array. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 46671a875b9..4955ad322a0 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o @@ -13,6 +14,7 @@ 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_ARIZONA_HAPTICS) += arizona-haptics.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o @@ -21,15 +23,20 @@ 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_DA9052_ONKEY) += da9052_onkey.o +obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o +obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o +obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.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_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o @@ -42,8 +49,11 @@ 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_RB532_BUTTON) += rb532_button.o +obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.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_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o @@ -53,3 +63,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 350fd0c385d..95ef7dd6442 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -7,12 +7,14 @@ * AB8500 Power-On Key handler */ +#include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/mfd/abx500/ab8500.h> +#include <linux/of.h> #include <linux/slab.h> /** @@ -44,7 +46,7 @@ static irqreturn_t ab8500_ponkey_handler(int irq, void *data) return IRQ_HANDLED; } -static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) +static int ab8500_ponkey_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_ponkey *ponkey; @@ -64,12 +66,14 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) return irq_dbr; } - ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); - input = input_allocate_device(); - if (!ponkey || !input) { - error = -ENOMEM; - goto err_free_mem; - } + ponkey = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_ponkey), + GFP_KERNEL); + if (!ponkey) + return -ENOMEM; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; ponkey->idev = input; ponkey->ab8500 = ab8500; @@ -81,63 +85,48 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbf", ponkey); + error = devm_request_any_context_irq(&pdev->dev, ponkey->irq_dbf, + ab8500_ponkey_handler, 0, + "ab8500-ponkey-dbf", ponkey); if (error < 0) { dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", ponkey->irq_dbf, error); - goto err_free_mem; + return error; } - error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbr", ponkey); + error = devm_request_any_context_irq(&pdev->dev, ponkey->irq_dbr, + ab8500_ponkey_handler, 0, + "ab8500-ponkey-dbr", ponkey); if (error < 0) { dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", ponkey->irq_dbr, error); - goto err_free_dbf_irq; + return error; } error = input_register_device(ponkey->idev); if (error) { dev_err(ab8500->dev, "Can't register input device: %d\n", error); - goto err_free_dbr_irq; + return error; } platform_set_drvdata(pdev, ponkey); return 0; - -err_free_dbr_irq: - free_irq(ponkey->irq_dbr, ponkey); -err_free_dbf_irq: - free_irq(ponkey->irq_dbf, ponkey); -err_free_mem: - input_free_device(input); - kfree(ponkey); - - return error; } -static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) -{ - struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); - - free_irq(ponkey->irq_dbf, ponkey); - free_irq(ponkey->irq_dbr, ponkey); - input_unregister_device(ponkey->idev); - kfree(ponkey); - - platform_set_drvdata(pdev, NULL); - - return 0; -} +#ifdef CONFIG_OF +static const struct of_device_id ab8500_ponkey_match[] = { + { .compatible = "stericsson,ab8500-ponkey", }, + {} +}; +#endif static struct platform_driver ab8500_ponkey_driver = { .driver = { .name = "ab8500-poweron-key", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ab8500_ponkey_match), }, .probe = ab8500_ponkey_probe, - .remove = __devexit_p(ab8500_ponkey_remove), }; module_platform_driver(ab8500_ponkey_driver); diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 56810fb4ead..e0f522516ef 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -13,7 +13,7 @@ #include <linux/pm.h> #include "ad714x.h" -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ad714x_i2c_suspend(struct device *dev) { return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev))); @@ -72,7 +72,7 @@ static int ad714x_i2c_read(struct ad714x_chip *chip, return 0; } -static int __devinit ad714x_i2c_probe(struct i2c_client *client, +static int ad714x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ad714x_chip *chip; @@ -87,7 +87,7 @@ static int __devinit ad714x_i2c_probe(struct i2c_client *client, return 0; } -static int __devexit ad714x_i2c_remove(struct i2c_client *client) +static int ad714x_i2c_remove(struct i2c_client *client) { struct ad714x_chip *chip = i2c_get_clientdata(client); @@ -112,21 +112,11 @@ static struct i2c_driver ad714x_i2c_driver = { .pm = &ad714x_i2c_pm, }, .probe = ad714x_i2c_probe, - .remove = __devexit_p(ad714x_i2c_remove), + .remove = ad714x_i2c_remove, .id_table = ad714x_id, }; -static int __init ad714x_i2c_init(void) -{ - return i2c_add_driver(&ad714x_i2c_driver); -} -module_init(ad714x_i2c_init); - -static void __exit ad714x_i2c_exit(void) -{ - i2c_del_driver(&ad714x_i2c_driver); -} -module_exit(ad714x_i2c_exit); +module_i2c_driver(ad714x_i2c_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index 875b5081136..3a90b710e30 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -16,7 +16,7 @@ #define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */ #define AD714x_SPI_READ BIT(10) -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ad714x_spi_suspend(struct device *dev) { return ad714x_disable(spi_get_drvdata(to_spi_device(dev))); @@ -83,7 +83,7 @@ static int ad714x_spi_write(struct ad714x_chip *chip, return 0; } -static int __devinit ad714x_spi_probe(struct spi_device *spi) +static int ad714x_spi_probe(struct spi_device *spi) { struct ad714x_chip *chip; int err; @@ -103,12 +103,11 @@ static int __devinit ad714x_spi_probe(struct spi_device *spi) return 0; } -static int __devexit ad714x_spi_remove(struct spi_device *spi) +static int ad714x_spi_remove(struct spi_device *spi) { struct ad714x_chip *chip = spi_get_drvdata(spi); ad714x_remove(chip); - spi_set_drvdata(spi, NULL); return 0; } @@ -120,20 +119,10 @@ static struct spi_driver ad714x_spi_driver = { .pm = &ad714x_spi_pm, }, .probe = ad714x_spi_probe, - .remove = __devexit_p(ad714x_spi_remove), + .remove = ad714x_spi_remove, }; -static __init int ad714x_spi_init(void) -{ - return spi_register_driver(&ad714x_spi_driver); -} -module_init(ad714x_spi_init); - -static __exit void ad714x_spi_exit(void) -{ - spi_unregister_driver(&ad714x_spi_driver); -} -module_exit(ad714x_spi_exit); +module_spi_driver(ad714x_spi_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index 0ac75bbad4d..7a61e9ee682 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -7,7 +7,6 @@ */ #include <linux/device.h> -#include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -969,9 +968,10 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, int error; struct input_dev *input[MAX_DEVICE_NUM]; - struct ad714x_platform_data *plat_data = dev->platform_data; + struct ad714x_platform_data *plat_data = dev_get_platdata(dev); struct ad714x_chip *ad714x; void *drv_mem; + unsigned long irqflags; struct ad714x_button_drv *bt_drv; struct ad714x_slider_drv *sd_drv; @@ -985,7 +985,7 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, goto err_out; } - if (dev->platform_data == NULL) { + if (dev_get_platdata(dev) == NULL) { dev_err(dev, "platform data for ad714x doesn't exist\n"); error = -EINVAL; goto err_out; @@ -1162,10 +1162,11 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, alloc_idx++; } + irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread, - plat_data->irqflags ? - plat_data->irqflags : IRQF_TRIGGER_FALLING, - "ad714x_captouch", ad714x); + irqflags, "ad714x_captouch", ad714x); if (error) { dev_err(dev, "can't allocate irq %d\n", ad714x->irq); goto err_unreg_dev; diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index ccacf2bb06a..416f47ddcc9 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -73,7 +73,7 @@ static const struct adxl34x_bus_ops adxl34x_i2c_bops = { .read_block = adxl34x_i2c_read_block, }; -static int __devinit adxl34x_i2c_probe(struct i2c_client *client, +static int adxl34x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adxl34x *ac; @@ -98,14 +98,14 @@ static int __devinit adxl34x_i2c_probe(struct i2c_client *client, return 0; } -static int __devexit adxl34x_i2c_remove(struct i2c_client *client) +static int adxl34x_i2c_remove(struct i2c_client *client) { struct adxl34x *ac = i2c_get_clientdata(client); return adxl34x_remove(ac); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int adxl34x_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -144,21 +144,11 @@ static struct i2c_driver adxl34x_driver = { .pm = &adxl34x_i2c_pm, }, .probe = adxl34x_i2c_probe, - .remove = __devexit_p(adxl34x_i2c_remove), + .remove = adxl34x_i2c_remove, .id_table = adxl34x_id, }; -static int __init adxl34x_i2c_init(void) -{ - return i2c_add_driver(&adxl34x_driver); -} -module_init(adxl34x_i2c_init); - -static void __exit adxl34x_i2c_exit(void) -{ - i2c_del_driver(&adxl34x_driver); -} -module_exit(adxl34x_i2c_exit); +module_i2c_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index 34d401efd4a..76dc0679d3b 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -65,7 +65,7 @@ static const struct adxl34x_bus_ops adxl34x_spi_bops = { .read_block = adxl34x_spi_read_block, }; -static int __devinit adxl34x_spi_probe(struct spi_device *spi) +static int adxl34x_spi_probe(struct spi_device *spi) { struct adxl34x *ac; @@ -87,18 +87,18 @@ static int __devinit adxl34x_spi_probe(struct spi_device *spi) return 0; } -static int __devexit adxl34x_spi_remove(struct spi_device *spi) +static int adxl34x_spi_remove(struct spi_device *spi) { - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); return adxl34x_remove(ac); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int adxl34x_spi_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); adxl34x_suspend(ac); @@ -108,7 +108,7 @@ static int adxl34x_spi_suspend(struct device *dev) static int adxl34x_spi_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); adxl34x_resume(ac); @@ -126,20 +126,10 @@ static struct spi_driver adxl34x_driver = { .pm = &adxl34x_spi_pm, }, .probe = adxl34x_spi_probe, - .remove = __devexit_p(adxl34x_spi_remove), + .remove = adxl34x_spi_remove, }; -static int __init adxl34x_spi_init(void) -{ - return spi_register_driver(&adxl34x_driver); -} -module_init(adxl34x_spi_init); - -static void __exit adxl34x_spi_exit(void) -{ - spi_unregister_driver(&adxl34x_driver); -} -module_exit(adxl34x_spi_exit); +module_spi_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 1cf72fe513e..2b2d02f408b 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -8,7 +8,6 @@ */ #include <linux/device.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -158,7 +157,7 @@ /* ORIENT ADXL346 only */ #define ADXL346_2D_VALID (1 << 6) -#define ADXL346_2D_ORIENT(x) (((x) & 0x3) >> 4) +#define ADXL346_2D_ORIENT(x) (((x) & 0x30) >> 4) #define ADXL346_3D_VALID (1 << 3) #define ADXL346_3D_ORIENT(x) ((x) & 0x7) #define ADXL346_2D_PORTRAIT_POS 0 /* +X */ @@ -232,7 +231,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = { .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, - .fifo_mode = FIFO_STREAM, + .fifo_mode = ADXL_FIFO_STREAM, .watermark = 0, }; @@ -714,7 +713,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, ac->fifo_delay = fifo_delay_default; - pdata = dev->platform_data; + pdata = dev_get_platdata(dev); if (!pdata) { dev_dbg(dev, "No platform data: Using default initialization\n"); @@ -732,7 +731,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, mutex_init(&ac->mutex); input_dev->name = "ADXL34x accelerometer"; - revid = ac->bops->read(dev, DEVID); + revid = AC_READ(ac, DEVID); switch (revid) { case ID_ADXL345: @@ -809,7 +808,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) ac->fifo_delay = false; - ac->bops->write(dev, POWER_CTL, 0); + AC_WRITE(ac, POWER_CTL, 0); err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, @@ -827,7 +826,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, if (err) goto err_remove_attr; - AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); AC_WRITE(ac, OFSX, pdata->x_axis_offset); ac->hwcal.x = pdata->x_axis_offset; AC_WRITE(ac, OFSY, pdata->y_axis_offset); diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c new file mode 100644 index 00000000000..ef2e281b0a4 --- /dev/null +++ b/drivers/input/misc/arizona-haptics.c @@ -0,0 +1,236 @@ +/* + * 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> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/slab.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/pdata.h> +#include <linux/mfd/arizona/registers.h> + +struct arizona_haptics { + struct arizona *arizona; + struct input_dev *input_dev; + struct work_struct work; + + struct mutex mutex; + u8 intensity; +}; + +static void arizona_haptics_work(struct work_struct *work) +{ + struct arizona_haptics *haptics = container_of(work, + struct arizona_haptics, + work); + struct arizona *arizona = haptics->arizona; + int ret; + + if (!haptics->arizona->dapm) { + dev_err(arizona->dev, "No DAPM context\n"); + return; + } + + if (haptics->intensity) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_PHASE_2_INTENSITY, + ARIZONA_PHASE2_INTENSITY_MASK, + haptics->intensity); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set intensity: %d\n", + ret); + return; + } + + /* This enable sequence will be a noop if already enabled */ + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start haptics: %d\n", + ret); + return; + } + + ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", + ret); + return; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + return; + } + } else { + /* This disable sequence will be a noop if already enabled */ + ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", + ret); + return; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + return; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to stop haptics: %d\n", + ret); + return; + } + } +} + +static int arizona_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + struct arizona *arizona = haptics->arizona; + + if (!arizona->dapm) { + dev_err(arizona->dev, "No DAPM context\n"); + return -EBUSY; + } + + if (effect->u.rumble.strong_magnitude) { + /* Scale the magnitude into the range the device supports */ + if (arizona->pdata.hap_act) { + haptics->intensity = + effect->u.rumble.strong_magnitude >> 9; + if (effect->direction < 0x8000) + haptics->intensity += 0x7f; + } else { + haptics->intensity = + effect->u.rumble.strong_magnitude >> 8; + } + } else { + haptics->intensity = 0; + } + + schedule_work(&haptics->work); + + return 0; +} + +static void arizona_haptics_close(struct input_dev *input) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + + cancel_work_sync(&haptics->work); + + if (haptics->arizona->dapm) + snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); +} + +static int arizona_haptics_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct arizona_haptics *haptics; + int ret; + + haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->arizona = arizona; + + ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_ACT, arizona->pdata.hap_act); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", + ret); + return ret; + } + + INIT_WORK(&haptics->work, arizona_haptics_work); + + haptics->input_dev = input_allocate_device(); + if (haptics->input_dev == NULL) { + dev_err(arizona->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_drvdata(haptics->input_dev, haptics); + + haptics->input_dev->name = "arizona:haptics"; + haptics->input_dev->dev.parent = pdev->dev.parent; + haptics->input_dev->close = arizona_haptics_close; + __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); + + ret = input_ff_create_memless(haptics->input_dev, NULL, + arizona_haptics_play); + if (ret < 0) { + dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", + ret); + goto err_ialloc; + } + + ret = input_register_device(haptics->input_dev); + if (ret < 0) { + dev_err(arizona->dev, "couldn't register input device: %d\n", + ret); + goto err_iff; + } + + platform_set_drvdata(pdev, haptics); + + return 0; + +err_iff: + if (haptics->input_dev) + input_ff_destroy(haptics->input_dev); +err_ialloc: + input_free_device(haptics->input_dev); + + return ret; +} + +static int arizona_haptics_remove(struct platform_device *pdev) +{ + struct arizona_haptics *haptics = platform_get_drvdata(pdev); + + input_unregister_device(haptics->input_dev); + + return 0; +} + +static struct platform_driver arizona_haptics_driver = { + .probe = arizona_haptics_probe, + .remove = arizona_haptics_remove, + .driver = { + .name = "arizona-haptics", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(arizona_haptics_driver); + +MODULE_ALIAS("platform:arizona-haptics"); +MODULE_DESCRIPTION("Arizona haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 601f7372f9c..638165c78e7 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -25,11 +25,10 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/input.h> #include <linux/types.h> +#include <linux/acpi.h> #include <asm/uaccess.h> -#include <acpi/acpi_drivers.h> #define ACPI_ATLAS_NAME "Atlas ACPI" #define ACPI_ATLAS_CLASS "Atlas" @@ -121,7 +120,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static int atlas_acpi_button_remove(struct acpi_device *device, int type) +static int atlas_acpi_button_remove(struct acpi_device *device) { acpi_status status; @@ -151,22 +150,7 @@ static struct acpi_driver atlas_acpi_driver = { .remove = atlas_acpi_button_remove, }, }; - -static int __init atlas_acpi_init(void) -{ - if (acpi_disabled) - return -ENODEV; - - return acpi_bus_register_driver(&atlas_acpi_driver); -} - -static void __exit atlas_acpi_exit(void) -{ - acpi_bus_unregister_driver(&atlas_acpi_driver); -} - -module_init(atlas_acpi_init); -module_exit(atlas_acpi_exit); +module_acpi_driver(atlas_acpi_driver); MODULE_AUTHOR("Jaya Kumar"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c index 1c4146fccfd..e69d9bcb37e 100644 --- a/drivers/input/misc/bfin_rotary.c +++ b/drivers/input/misc/bfin_rotary.c @@ -6,7 +6,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/pm.h> @@ -90,9 +89,9 @@ static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit bfin_rotary_probe(struct platform_device *pdev) +static int bfin_rotary_probe(struct platform_device *pdev) { - struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data; + struct bfin_rotary_platform_data *pdata = dev_get_platdata(&pdev->dev); struct bfin_rot *rotary; struct input_dev *input; int error; @@ -196,7 +195,7 @@ out1: return error; } -static int __devexit bfin_rotary_remove(struct platform_device *pdev) +static int bfin_rotary_remove(struct platform_device *pdev) { struct bfin_rot *rotary = platform_get_drvdata(pdev); @@ -208,7 +207,6 @@ static int __devexit bfin_rotary_remove(struct platform_device *pdev) peripheral_free_list(per_cnt); kfree(rotary); - platform_set_drvdata(pdev, NULL); return 0; } @@ -255,7 +253,7 @@ static const struct dev_pm_ops bfin_rotary_pm_ops = { static struct platform_driver bfin_rotary_device_driver = { .probe = bfin_rotary_probe, - .remove = __devexit_p(bfin_rotary_remove), + .remove = bfin_rotary_remove, .driver = { .name = "bfin-rotary", .owner = THIS_MODULE, diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 8f55b54352b..b36831c828d 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -46,18 +46,6 @@ #define BMA150_POLL_MAX 200 #define BMA150_POLL_MIN 0 -#define BMA150_BW_25HZ 0 -#define BMA150_BW_50HZ 1 -#define BMA150_BW_100HZ 2 -#define BMA150_BW_190HZ 3 -#define BMA150_BW_375HZ 4 -#define BMA150_BW_750HZ 5 -#define BMA150_BW_1500HZ 6 - -#define BMA150_RANGE_2G 0 -#define BMA150_RANGE_4G 1 -#define BMA150_RANGE_8G 2 - #define BMA150_MODE_NORMAL 0 #define BMA150_MODE_SLEEP 2 #define BMA150_MODE_WAKE_UP 3 @@ -82,6 +70,7 @@ #define BMA150_CFG_5_REG 0x11 #define BMA150_CHIP_ID 2 +#define BMA180_CHIP_ID 3 #define BMA150_CHIP_ID_REG BMA150_DATA_0_REG #define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG @@ -158,7 +147,7 @@ struct bma150_data { * are stated and verified by Bosch Sensortec where they are configured * to provide a generic sensitivity performance. */ -static struct bma150_cfg default_cfg __devinitdata = { +static struct bma150_cfg default_cfg = { .any_motion_int = 1, .hg_int = 1, .lg_int = 1, @@ -224,7 +213,7 @@ static int bma150_set_mode(struct bma150_data *bma150, u8 mode) return 0; } -static int __devinit bma150_soft_reset(struct bma150_data *bma150) +static int bma150_soft_reset(struct bma150_data *bma150) { int error; @@ -237,19 +226,19 @@ static int __devinit bma150_soft_reset(struct bma150_data *bma150) return 0; } -static int __devinit bma150_set_range(struct bma150_data *bma150, u8 range) +static int bma150_set_range(struct bma150_data *bma150, u8 range) { return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS, BMA150_RANGE_MSK, BMA150_RANGE_REG); } -static int __devinit bma150_set_bandwidth(struct bma150_data *bma150, u8 bw) +static int bma150_set_bandwidth(struct bma150_data *bma150, u8 bw) { return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS, BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG); } -static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150, +static int bma150_set_low_g_interrupt(struct bma150_data *bma150, u8 enable, u8 hyst, u8 dur, u8 thres) { int error; @@ -273,7 +262,7 @@ static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150, BMA150_LOW_G_EN_REG); } -static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150, +static int bma150_set_high_g_interrupt(struct bma150_data *bma150, u8 enable, u8 hyst, u8 dur, u8 thres) { int error; @@ -300,7 +289,7 @@ static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150, } -static int __devinit bma150_set_any_motion_interrupt(struct bma150_data *bma150, +static int bma150_set_any_motion_interrupt(struct bma150_data *bma150, u8 enable, u8 dur, u8 thres) { int error; @@ -372,7 +361,7 @@ static int bma150_open(struct bma150_data *bma150) int error; error = pm_runtime_get_sync(&bma150->client->dev); - if (error && error != -ENOSYS) + if (error < 0 && error != -ENOSYS) return error; /* @@ -424,7 +413,7 @@ static void bma150_poll_close(struct input_polled_dev *ipoll_dev) bma150_close(bma150); } -static int __devinit bma150_initialize(struct bma150_data *bma150, +static int bma150_initialize(struct bma150_data *bma150, const struct bma150_cfg *cfg) { int error; @@ -465,7 +454,7 @@ static int __devinit bma150_initialize(struct bma150_data *bma150, return bma150_set_mode(bma150, BMA150_MODE_SLEEP); } -static void __devinit bma150_init_input_device(struct bma150_data *bma150, +static void bma150_init_input_device(struct bma150_data *bma150, struct input_dev *idev) { idev->name = BMA150_DRIVER; @@ -479,7 +468,7 @@ static void __devinit bma150_init_input_device(struct bma150_data *bma150, input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); } -static int __devinit bma150_register_input_device(struct bma150_data *bma150) +static int bma150_register_input_device(struct bma150_data *bma150) { struct input_dev *idev; int error; @@ -504,7 +493,7 @@ static int __devinit bma150_register_input_device(struct bma150_data *bma150) return 0; } -static int __devinit bma150_register_polled_device(struct bma150_data *bma150) +static int bma150_register_polled_device(struct bma150_data *bma150) { struct input_polled_dev *ipoll_dev; int error; @@ -535,10 +524,11 @@ static int __devinit bma150_register_polled_device(struct bma150_data *bma150) return 0; } -static int __devinit bma150_probe(struct i2c_client *client, +static int bma150_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct bma150_platform_data *pdata = client->dev.platform_data; + const struct bma150_platform_data *pdata = + dev_get_platdata(&client->dev); const struct bma150_cfg *cfg; struct bma150_data *bma150; int chip_id; @@ -550,7 +540,7 @@ static int __devinit bma150_probe(struct i2c_client *client, } chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); - if (chip_id != BMA150_CHIP_ID) { + if (chip_id != BMA150_CHIP_ID && chip_id != BMA180_CHIP_ID) { dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); return -EINVAL; } @@ -613,7 +603,7 @@ err_free_mem: return error; } -static int __devexit bma150_remove(struct i2c_client *client) +static int bma150_remove(struct i2c_client *client) { struct bma150_data *bma150 = i2c_get_clientdata(client); @@ -654,6 +644,7 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); static const struct i2c_device_id bma150_id[] = { { "bma150", 0 }, + { "bma180", 0 }, { "smb380", 0 }, { "bma023", 0 }, { } @@ -670,22 +661,11 @@ static struct i2c_driver bma150_driver = { .class = I2C_CLASS_HWMON, .id_table = bma150_id, .probe = bma150_probe, - .remove = __devexit_p(bma150_remove), + .remove = bma150_remove, }; -static int __init BMA150_init(void) -{ - return i2c_add_driver(&bma150_driver); -} - -static void __exit BMA150_exit(void) -{ - i2c_del_driver(&bma150_driver); -} +module_i2c_driver(bma150_driver); MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>"); MODULE_DESCRIPTION("BMA150 driver"); MODULE_LICENSE("GPL"); - -module_init(BMA150_init); -module_exit(BMA150_exit); diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index ab860511f01..9365535ba7f 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -327,7 +327,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev) error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); if (error) - err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); + dev_err(&dev->intf->dev, + "%s: usb_submit_urb (urb_ctl) failed %d\n", + __func__, error); } /* @@ -339,7 +341,7 @@ static void cm109_urb_irq_callback(struct urb *urb) const int status = urb->status; int error; - dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", + dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", dev->irq_data->byte[0], dev->irq_data->byte[1], dev->irq_data->byte[2], @@ -349,7 +351,9 @@ static void cm109_urb_irq_callback(struct urb *urb) if (status) { if (status == -ESHUTDOWN) return; - err("%s: urb status %d", __func__, status); + dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n", + __func__, status); + goto out; } /* Special keys */ @@ -396,7 +400,8 @@ static void cm109_urb_irq_callback(struct urb *urb) error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); if (error) - err("%s: usb_submit_urb (urb_ctl) failed %d", + dev_err(&dev->intf->dev, + "%s: usb_submit_urb (urb_ctl) failed %d\n", __func__, error); } @@ -409,14 +414,18 @@ static void cm109_urb_ctl_callback(struct urb *urb) const int status = urb->status; int error; - dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n", + dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n", dev->ctl_data->byte[0], dev->ctl_data->byte[1], dev->ctl_data->byte[2], dev->ctl_data->byte[3]); - if (status) - err("%s: urb status %d", __func__, status); + if (status) { + if (status == -ESHUTDOWN) + return; + dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n", + __func__, status); + } spin_lock(&dev->ctl_submit_lock); @@ -424,7 +433,7 @@ static void cm109_urb_ctl_callback(struct urb *urb) if (likely(!dev->shutdown)) { - if (dev->buzzer_pending) { + if (dev->buzzer_pending || status) { dev->buzzer_pending = 0; dev->ctl_urb_pending = 1; cm109_submit_buzz_toggle(dev); @@ -433,7 +442,8 @@ static void cm109_urb_ctl_callback(struct urb *urb) dev->irq_urb_pending = 1; error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC); if (error) - err("%s: usb_submit_urb (urb_irq) failed %d", + dev_err(&dev->intf->dev, + "%s: usb_submit_urb (urb_irq) failed %d\n", __func__, error); } } @@ -476,7 +486,8 @@ static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on) dev->ctl_data, USB_PKT_LEN, USB_CTRL_SET_TIMEOUT); if (error < 0 && error != -EINTR) - err("%s: usb_control_msg() failed %d", __func__, error); + dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n", + __func__, error); } static void cm109_stop_traffic(struct cm109_dev *dev) @@ -518,8 +529,8 @@ static int cm109_input_open(struct input_dev *idev) error = usb_autopm_get_interface(dev->intf); if (error < 0) { - err("%s - cannot autoresume, result %d", - __func__, error); + dev_err(&idev->dev, "%s - cannot autoresume, result %d\n", + __func__, error); return error; } @@ -537,7 +548,8 @@ static int cm109_input_open(struct input_dev *idev) error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL); if (error) - err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); + dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n", + __func__, error); else dev->open = 1; @@ -573,7 +585,7 @@ static int cm109_input_ev(struct input_dev *idev, unsigned int type, { struct cm109_dev *dev = input_get_drvdata(idev); - dev_dbg(&dev->udev->dev, + dev_dbg(&dev->intf->dev, "input_ev: type=%u code=%u value=%d\n", type, code, value); if (type != EV_SND) @@ -710,7 +722,8 @@ static int cm109_usb_probe(struct usb_interface *intf, pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); if (ret != USB_PKT_LEN) - err("invalid payload size %d, expected %d", ret, USB_PKT_LEN); + dev_err(&intf->dev, "invalid payload size %d, expected %d\n", + ret, USB_PKT_LEN); /* initialise irq urb */ usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data, diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c index 06517e60e50..c7d00748277 100644 --- a/drivers/input/misc/cma3000_d0x.c +++ b/drivers/input/misc/cma3000_d0x.c @@ -58,7 +58,7 @@ /* * Bit weights in mg for bit 0, other bits need - * multipy factor 2^n. Eight bit is the sign bit. + * multiply factor 2^n. Eight bit is the sign bit. */ #define BIT_TO_2G 18 #define BIT_TO_8G 71 @@ -284,7 +284,7 @@ EXPORT_SYMBOL(cma3000_resume); struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, const struct cma3000_bus_ops *bops) { - const struct cma3000_platform_data *pdata = dev->platform_data; + const struct cma3000_platform_data *pdata = dev_get_platdata(dev); struct cma3000_accl_data *data; struct input_dev *input_dev; int rev; @@ -318,7 +318,7 @@ struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, mutex_init(&data->mutex); data->mode = pdata->mode; - if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) { + if (data->mode > CMAMODE_POFF) { data->mode = CMAMODE_MOTDET; dev_warn(dev, "Invalid mode specified, assuming Motion Detect\n"); diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index d100cc5c578..4fdef98ceb5 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -55,7 +55,7 @@ static const struct cma3000_bus_ops cma3000_i2c_bops = { .write = cma3000_i2c_set, }; -static int __devinit cma3000_i2c_probe(struct i2c_client *client, +static int cma3000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct cma3000_accl_data *data; @@ -69,7 +69,7 @@ static int __devinit cma3000_i2c_probe(struct i2c_client *client, return 0; } -static int __devexit cma3000_i2c_remove(struct i2c_client *client) +static int cma3000_i2c_remove(struct i2c_client *client) { struct cma3000_accl_data *data = i2c_get_clientdata(client); @@ -114,7 +114,7 @@ MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id); static struct i2c_driver cma3000_i2c_driver = { .probe = cma3000_i2c_probe, - .remove = __devexit_p(cma3000_i2c_remove), + .remove = cma3000_i2c_remove, .id_table = cma3000_i2c_id, .driver = { .name = "cma3000_i2c_accl", @@ -125,18 +125,7 @@ static struct i2c_driver cma3000_i2c_driver = { }, }; -static int __init cma3000_i2c_init(void) -{ - return i2c_add_driver(&cma3000_i2c_driver); -} - -static void __exit cma3000_i2c_exit(void) -{ - i2c_del_driver(&cma3000_i2c_driver); -} - -module_init(cma3000_i2c_init); -module_exit(cma3000_i2c_exit); +module_i2c_driver(cma3000_i2c_driver); MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index 53e43d29514..3e11510ff82 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -17,7 +17,6 @@ * 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/init.h> #include <linux/input-polldev.h> #include <linux/ioport.h> #include <linux/module.h> @@ -73,7 +72,7 @@ static void handle_buttons(struct input_polled_dev *dev) } } -static int __devinit cobalt_buttons_probe(struct platform_device *pdev) +static int cobalt_buttons_probe(struct platform_device *pdev) { struct buttons_dev *bdev; struct input_polled_dev *poll_dev; @@ -131,11 +130,10 @@ static int __devinit cobalt_buttons_probe(struct platform_device *pdev) err_free_mem: input_free_polled_device(poll_dev); kfree(bdev); - dev_set_drvdata(&pdev->dev, NULL); return error; } -static int __devexit cobalt_buttons_remove(struct platform_device *pdev) +static int cobalt_buttons_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct buttons_dev *bdev = dev_get_drvdata(dev); @@ -144,7 +142,6 @@ static int __devexit cobalt_buttons_remove(struct platform_device *pdev) input_free_polled_device(bdev->poll_dev); iounmap(bdev->reg); kfree(bdev); - dev_set_drvdata(dev, NULL); return 0; } @@ -157,7 +154,7 @@ MODULE_ALIAS("platform:Cobalt buttons"); static struct platform_driver cobalt_buttons_driver = { .probe = cobalt_buttons_probe, - .remove = __devexit_p(cobalt_buttons_remove), + .remove = cobalt_buttons_remove, .driver = { .name = "Cobalt buttons", .owner = THIS_MODULE, diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c new file mode 100644 index 00000000000..184c8f21ab5 --- /dev/null +++ b/drivers/input/misc/da9052_onkey.c @@ -0,0 +1,160 @@ +/* + * 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> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +struct da9052_onkey { + struct da9052 *da9052; + struct input_dev *input; + struct delayed_work work; +}; + +static void da9052_onkey_query(struct da9052_onkey *onkey) +{ + int ret; + + ret = da9052_reg_read(onkey->da9052, DA9052_STATUS_A_REG); + if (ret < 0) { + dev_err(onkey->da9052->dev, + "Failed to read onkey event err=%d\n", ret); + } else { + /* + * Since interrupt for deassertion of ONKEY pin is not + * generated, onkey event state determines the onkey + * button state. + */ + bool pressed = !(ret & DA9052_STATUSA_NONKEY); + + input_report_key(onkey->input, KEY_POWER, pressed); + input_sync(onkey->input); + + /* + * Interrupt is generated only when the ONKEY pin + * is asserted. Hence the deassertion of the pin + * is simulated through work queue. + */ + if (pressed) + schedule_delayed_work(&onkey->work, + msecs_to_jiffies(50)); + } +} + +static void da9052_onkey_work(struct work_struct *work) +{ + struct da9052_onkey *onkey = container_of(work, struct da9052_onkey, + work.work); + + da9052_onkey_query(onkey); +} + +static irqreturn_t da9052_onkey_irq(int irq, void *data) +{ + struct da9052_onkey *onkey = data; + + da9052_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int da9052_onkey_probe(struct platform_device *pdev) +{ + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct da9052_onkey *onkey; + struct input_dev *input_dev; + int error; + + if (!da9052) { + dev_err(&pdev->dev, "Failed to get the driver's data\n"); + return -EINVAL; + } + + onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!onkey || !input_dev) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + onkey->input = input_dev; + onkey->da9052 = da9052; + INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); + + input_dev->name = "da9052-onkey"; + input_dev->phys = "da9052-onkey/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, input_dev->keybit); + + error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY", + da9052_onkey_irq, onkey); + if (error < 0) { + dev_err(onkey->da9052->dev, + "Failed to register ONKEY IRQ: %d\n", error); + goto err_free_mem; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, "Unable to register input device, %d\n", + error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, onkey); + return 0; + +err_free_irq: + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); + cancel_delayed_work_sync(&onkey->work); +err_free_mem: + input_free_device(input_dev); + kfree(onkey); + + return error; +} + +static int da9052_onkey_remove(struct platform_device *pdev) +{ + struct da9052_onkey *onkey = platform_get_drvdata(pdev); + + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); + cancel_delayed_work_sync(&onkey->work); + + 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 = { + .name = "da9052-onkey", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(da9052_onkey_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("Onkey driver for DA9052"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-onkey"); diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c new file mode 100644 index 00000000000..4765799fef7 --- /dev/null +++ b/drivers/input/misc/da9055_onkey.c @@ -0,0 +1,169 @@ +/* + * 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> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <linux/mfd/da9055/core.h> +#include <linux/mfd/da9055/reg.h> + +struct da9055_onkey { + struct da9055 *da9055; + struct input_dev *input; + struct delayed_work work; +}; + +static void da9055_onkey_query(struct da9055_onkey *onkey) +{ + int key_stat; + + key_stat = da9055_reg_read(onkey->da9055, DA9055_REG_STATUS_A); + if (key_stat < 0) { + dev_err(onkey->da9055->dev, + "Failed to read onkey event %d\n", key_stat); + } else { + key_stat &= DA9055_NOKEY_STS; + /* + * Onkey status bit is cleared when onkey button is released. + */ + if (!key_stat) { + input_report_key(onkey->input, KEY_POWER, 0); + input_sync(onkey->input); + } + } + + /* + * Interrupt is generated only when the ONKEY pin is asserted. + * Hence the deassertion of the pin is simulated through work queue. + */ + if (key_stat) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(10)); + +} + +static void da9055_onkey_work(struct work_struct *work) +{ + struct da9055_onkey *onkey = container_of(work, struct da9055_onkey, + work.work); + + da9055_onkey_query(onkey); +} + +static irqreturn_t da9055_onkey_irq(int irq, void *data) +{ + struct da9055_onkey *onkey = data; + + input_report_key(onkey->input, KEY_POWER, 1); + input_sync(onkey->input); + + da9055_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int da9055_onkey_probe(struct platform_device *pdev) +{ + struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent); + struct da9055_onkey *onkey; + struct input_dev *input_dev; + 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); + return -EINVAL; + } + + onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + onkey->input = input_dev; + onkey->da9055 = da9055; + input_dev->name = "da9055-onkey"; + input_dev->phys = "da9055-onkey/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, input_dev->keybit); + + INIT_DELAYED_WORK(&onkey->work, da9055_onkey_work); + + err = request_threaded_irq(irq, NULL, da9055_onkey_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ONKEY", onkey); + if (err < 0) { + dev_err(&pdev->dev, + "Failed to register ONKEY IRQ %d, error = %d\n", + irq, err); + goto err_free_input; + } + + err = input_register_device(input_dev); + if (err) { + dev_err(&pdev->dev, "Unable to register input device, %d\n", + err); + goto err_free_irq; + } + + platform_set_drvdata(pdev, onkey); + + return 0; + +err_free_irq: + free_irq(irq, onkey); + cancel_delayed_work_sync(&onkey->work); +err_free_input: + input_free_device(input_dev); + + return err; +} + +static int da9055_onkey_remove(struct platform_device *pdev) +{ + struct da9055_onkey *onkey = platform_get_drvdata(pdev); + int irq = platform_get_irq_byname(pdev, "ONKEY"); + + irq = regmap_irq_get_virq(onkey->da9055->irq_data, irq); + 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 = { + .name = "da9055-onkey", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9055_onkey_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("Onkey driver for DA9055"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-onkey"); diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index 35083c6836c..0eba94f581d 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> @@ -173,7 +172,7 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) /*----------------------------------------------------------------------*/ -static int __devinit dm355evm_keys_probe(struct platform_device *pdev) +static int dm355evm_keys_probe(struct platform_device *pdev) { struct dm355evm_keys *keys; struct input_dev *input; @@ -213,7 +212,8 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) /* REVISIT: flush the event queue? */ status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&pdev->dev), keys); if (status < 0) goto fail2; @@ -238,7 +238,7 @@ fail1: return status; } -static int __devexit dm355evm_keys_remove(struct platform_device *pdev) +static int dm355evm_keys_remove(struct platform_device *pdev) { struct dm355evm_keys *keys = platform_get_drvdata(pdev); @@ -261,7 +261,7 @@ static int __devexit dm355evm_keys_remove(struct platform_device *pdev) */ static struct platform_driver dm355evm_keys_driver = { .probe = dm355evm_keys_probe, - .remove = __devexit_p(dm355evm_keys_remove), + .remove = dm355evm_keys_remove, .driver = { .owner = THIS_MODULE, .name = "dm355evm_keys", diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c index 71fba8c2fc6..de21e317da3 100644 --- a/drivers/input/misc/gp2ap002a00f.c +++ b/drivers/input/misc/gp2ap002a00f.c @@ -98,7 +98,7 @@ static void gp2a_device_close(struct input_dev *dev) "unable to deactivate, err %d\n", error); } -static int __devinit gp2a_initialize(struct gp2a_data *dt) +static int gp2a_initialize(struct gp2a_data *dt) { int error; @@ -122,10 +122,10 @@ static int __devinit gp2a_initialize(struct gp2a_data *dt) return error; } -static int __devinit gp2a_probe(struct i2c_client *client, +static int gp2a_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct gp2a_platform_data *pdata = client->dev.platform_data; + const struct gp2a_platform_data *pdata = dev_get_platdata(&client->dev); struct gp2a_data *dt; int error; @@ -205,7 +205,7 @@ err_hw_shutdown: return error; } -static int __devexit gp2a_remove(struct i2c_client *client) +static int gp2a_remove(struct i2c_client *client) { struct gp2a_data *dt = i2c_get_clientdata(client); const struct gp2a_platform_data *pdata = dt->pdata; @@ -277,22 +277,11 @@ static struct i2c_driver gp2a_i2c_driver = { .pm = &gp2a_pm, }, .probe = gp2a_probe, - .remove = __devexit_p(gp2a_remove), + .remove = gp2a_remove, .id_table = gp2a_i2c_id, }; -static int __init gp2a_init(void) -{ - return i2c_add_driver(&gp2a_i2c_driver); -} - -static void __exit gp2a_exit(void) -{ - i2c_del_driver(&gp2a_i2c_driver); -} - -module_init(gp2a_init); -module_exit(gp2a_exit); +module_i2c_driver(gp2a_i2c_driver); MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>"); MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c new file mode 100644 index 00000000000..8886af63eae --- /dev/null +++ b/drivers/input/misc/gpio-beeper.c @@ -0,0 +1,124 @@ +/* + * 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> +#include <linux/module.h> +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#define BEEPER_MODNAME "gpio-beeper" + +struct gpio_beeper { + struct work_struct work; + struct gpio_desc *desc; + bool beeping; +}; + +static void gpio_beeper_toggle(struct gpio_beeper *beep, bool on) +{ + gpiod_set_value_cansleep(beep->desc, on); +} + +static void gpio_beeper_work(struct work_struct *work) +{ + struct gpio_beeper *beep = container_of(work, struct gpio_beeper, work); + + gpio_beeper_toggle(beep, beep->beeping); +} + +static int gpio_beeper_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct gpio_beeper *beep = input_get_drvdata(dev); + + if (type != EV_SND || code != SND_BELL) + return -ENOTSUPP; + + if (value < 0) + return -EINVAL; + + beep->beeping = value; + /* Schedule work to actually turn the beeper on or off */ + schedule_work(&beep->work); + + return 0; +} + +static void gpio_beeper_close(struct input_dev *input) +{ + struct gpio_beeper *beep = input_get_drvdata(input); + + cancel_work_sync(&beep->work); + gpio_beeper_toggle(beep, false); +} + +static int gpio_beeper_probe(struct platform_device *pdev) +{ + struct gpio_beeper *beep; + struct input_dev *input; + int err; + + beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL); + if (!beep) + return -ENOMEM; + + beep->desc = devm_gpiod_get(&pdev->dev, NULL); + if (IS_ERR(beep->desc)) + return PTR_ERR(beep->desc); + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + INIT_WORK(&beep->work, gpio_beeper_work); + + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + input->close = gpio_beeper_close; + input->event = gpio_beeper_event; + + input_set_capability(input, EV_SND, SND_BELL); + + err = gpiod_direction_output(beep->desc, 0); + if (err) + return err; + + input_set_drvdata(input, beep); + + return input_register_device(input); +} + +#ifdef CONFIG_OF +static const struct of_device_id gpio_beeper_of_match[] = { + { .compatible = BEEPER_MODNAME, }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_beeper_of_match); +#endif + +static struct platform_driver gpio_beeper_platform_driver = { + .driver = { + .name = BEEPER_MODNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_beeper_of_match), + }, + .probe = gpio_beeper_probe, +}; +module_platform_driver(gpio_beeper_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("Generic GPIO beeper driver"); diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c index 277a0574c19..1a81d911522 100644 --- a/drivers/input/misc/gpio_tilt_polled.c +++ b/drivers/input/misc/gpio_tilt_polled.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/input-polldev.h> @@ -96,9 +95,10 @@ static void gpio_tilt_polled_close(struct input_polled_dev *dev) pdata->disable(tdev->dev); } -static int __devinit gpio_tilt_polled_probe(struct platform_device *pdev) +static int gpio_tilt_polled_probe(struct platform_device *pdev) { - const struct gpio_tilt_platform_data *pdata = pdev->dev.platform_data; + const struct gpio_tilt_platform_data *pdata = + dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct gpio_tilt_polled_dev *tdev; struct input_polled_dev *poll_dev; @@ -179,13 +179,11 @@ err_free_tdev: return error; } -static int __devexit gpio_tilt_polled_remove(struct platform_device *pdev) +static int gpio_tilt_polled_remove(struct platform_device *pdev) { struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); const struct gpio_tilt_platform_data *pdata = tdev->pdata; - platform_set_drvdata(pdev, NULL); - input_unregister_polled_device(tdev->poll_dev); input_free_polled_device(tdev->poll_dev); @@ -198,7 +196,7 @@ static int __devexit gpio_tilt_polled_remove(struct platform_device *pdev) static struct platform_driver gpio_tilt_polled_driver = { .probe = gpio_tilt_polled_probe, - .remove = __devexit_p(gpio_tilt_polled_remove), + .remove = gpio_tilt_polled_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index 0b4f54265f6..45e0e3e55de 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -41,6 +41,7 @@ #include <linux/time.h> #include <linux/miscdevice.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/poll.h> #include <linux/rtc.h> #include <linux/mutex.h> @@ -74,9 +75,6 @@ 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 int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data); - static void hp_sdc_rtc_isr (int irq, void *dev_id, uint8_t status, uint8_t data) { @@ -109,7 +107,9 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm) if (hp_sdc_enqueue_transaction(&t)) return -1; - down_interruptible(&tsem); /* Put ourselves to sleep for results. */ + /* Put ourselves to sleep for results. */ + if (WARN_ON(down_interruptible(&tsem))) + return -1; /* Check for nonpresence of BBRTC */ if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] | @@ -176,11 +176,19 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg) t.seq = tseq; t.act.semaphore = &i8042tregs; - down_interruptible(&i8042tregs); /* Sleep if output regs in use. */ + /* Sleep if output regs in use. */ + if (WARN_ON(down_interruptible(&i8042tregs))) + return -1; - if (hp_sdc_enqueue_transaction(&t)) return -1; + if (hp_sdc_enqueue_transaction(&t)) { + up(&i8042tregs); + return -1; + } - down_interruptible(&i8042tregs); /* Sleep until results come back. */ + /* Sleep until results come back. */ + if (WARN_ON(down_interruptible(&i8042tregs))) + return -1; + up(&i8042tregs); return (tseq[5] | @@ -276,6 +284,7 @@ static inline int hp_sdc_rtc_read_ct(struct timeval *res) { } +#if 0 /* not used yet */ /* Set the i8042 real-time clock */ static int hp_sdc_rtc_set_rt (struct timeval *setto) { @@ -386,6 +395,7 @@ static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd) } return 0; } +#endif static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -418,22 +428,19 @@ 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_output (char *buf) +static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v) { #define YN(bit) ("no") #define NY(bit) ("yes") - char *p; struct rtc_time tm; struct timeval tv; memset(&tm, 0, sizeof(struct rtc_time)); - p = buf; - if (hp_sdc_rtc_read_bbrtc(&tm)) { - p += sprintf(p, "BBRTC\t\t: READ FAILED!\n"); + seq_puts(m, "BBRTC\t\t: READ FAILED!\n"); } else { - p += sprintf(p, + seq_printf(m, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" "rtc_epoch\t: %04lu\n", @@ -443,41 +450,41 @@ static int hp_sdc_rtc_proc_output (char *buf) } if (hp_sdc_rtc_read_rt(&tv)) { - p += sprintf(p, "i8042 rtc\t: READ FAILED!\n"); + seq_puts(m, "i8042 rtc\t: READ FAILED!\n"); } else { - p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", + seq_printf(m, "i8042 rtc\t: %ld.%02d seconds\n", tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_fhs(&tv)) { - p += sprintf(p, "handshake\t: READ FAILED!\n"); + seq_puts(m, "handshake\t: READ FAILED!\n"); } else { - p += sprintf(p, "handshake\t: %ld.%02d seconds\n", + seq_printf(m, "handshake\t: %ld.%02d seconds\n", tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_mt(&tv)) { - p += sprintf(p, "alarm\t\t: READ FAILED!\n"); + seq_puts(m, "alarm\t\t: READ FAILED!\n"); } else { - p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", + seq_printf(m, "alarm\t\t: %ld.%02d seconds\n", tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_dt(&tv)) { - p += sprintf(p, "delay\t\t: READ FAILED!\n"); + seq_puts(m, "delay\t\t: READ FAILED!\n"); } else { - p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", + seq_printf(m, "delay\t\t: %ld.%02d seconds\n", tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_ct(&tv)) { - p += sprintf(p, "periodic\t: READ FAILED!\n"); + seq_puts(m, "periodic\t: READ FAILED!\n"); } else { - p += sprintf(p, "periodic\t: %ld.%02d seconds\n", + seq_printf(m, "periodic\t: %ld.%02d seconds\n", tv.tv_sec, (int)tv.tv_usec/1000); } - p += sprintf(p, + seq_printf(m, "DST_enable\t: %s\n" "BCD\t\t: %s\n" "24hr\t\t: %s\n" @@ -497,23 +504,23 @@ static int hp_sdc_rtc_proc_output (char *buf) 1UL, 1 ? "okay" : "dead"); - return p - buf; + return 0; #undef YN #undef NY } -static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int hp_sdc_rtc_proc_open(struct inode *inode, struct file *file) { - int len = hp_sdc_rtc_proc_output (page); - if (len <= off+count) *eof = 1; - *start = page + off; - len -= off; - if (len>count) len = count; - if (len<0) len = 0; - return len; + return single_open(file, hp_sdc_rtc_proc_show, NULL); } +static const struct file_operations hp_sdc_rtc_proc_fops = { + .open = hp_sdc_rtc_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int hp_sdc_rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -706,8 +713,7 @@ static int __init hp_sdc_rtc_init(void) if (misc_register(&hp_sdc_rtc_dev) != 0) printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n"); - create_proc_read_entry ("driver/rtc", 0, NULL, - hp_sdc_rtc_read_proc, NULL); + proc_create("driver/rtc", 0, NULL, &hp_sdc_rtc_proc_fops); printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded " "(RTC v " RTC_VERSION ")\n"); diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c new file mode 100644 index 00000000000..edfd6239f13 --- /dev/null +++ b/drivers/input/misc/ideapad_slidebar.c @@ -0,0 +1,358 @@ +/* + * Input driver for slidebars on some Lenovo IdeaPad laptops + * + * Copyright (C) 2013 Andrey Moiseev <o2g.org.ru@gmail.com> + * + * 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. + */ + +/* + * Currently tested and works on: + * Lenovo IdeaPad Y550 + * Lenovo IdeaPad Y550P + * + * Other models can be added easily. To test, + * load with 'force' parameter set 'true'. + * + * LEDs blinking and input mode are managed via sysfs, + * (hex, unsigned byte value): + * /sys/devices/platform/ideapad_slidebar/slidebar_mode + * + * The value is in byte range, however, I only figured out + * how bits 0b10011001 work. Some other bits, probably, + * are meaningfull too. + * + * Possible states: + * + * STD_INT, ONMOV_INT, OFF_INT, LAST_POLL, OFF_POLL + * + * Meaning: + * released touched + * STD 'heartbeat' lights follow the finger + * ONMOV no lights lights follow the finger + * LAST at last pos lights follow the finger + * OFF no lights no lights + * + * INT all input events are generated, interrupts are used + * POLL no input events by default, to get them, + * send 0b10000000 (read below) + * + * Commands: write + * + * All | 0b01001 -> STD_INT + * possible | 0b10001 -> ONMOV_INT + * states | 0b01000 -> OFF_INT + * + * | 0b0 -> LAST_POLL + * STD_INT or ONMOV_INT | + * | 0b1 -> STD_INT + * + * | 0b0 -> OFF_POLL + * OFF_INT or OFF_POLL | + * | 0b1 -> OFF_INT + * + * Any state | 0b10000000 -> if the slidebar has updated data, + * produce one input event (last position), + * switch to respective POLL mode + * (like 0x0), if not in POLL mode yet. + * + * Get current state: read + * + * masked by 0x11 read value means: + * + * 0x00 LAST + * 0x01 STD + * 0x10 OFF + * 0x11 ONMOV + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dmi.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/i8042.h> +#include <linux/serio.h> + +#define IDEAPAD_BASE 0xff29 + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static DEFINE_SPINLOCK(io_lock); + +static struct input_dev *slidebar_input_dev; +static struct platform_device *slidebar_platform_dev; + +static u8 slidebar_pos_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf4, 0xff29); + outb(0xbf, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static u8 slidebar_mode_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf7, 0xff29); + outb(0x8b, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static void slidebar_mode_set(u8 mode) +{ + unsigned long flags; + + 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) +{ + static bool extended = false; + + /* We are only interested in data coming form KBC port */ + if (str & I8042_STR_AUXDATA) + return false; + + /* Scancodes: e03b on move, e0bb on release. */ + if (data == 0xe0) { + extended = true; + return true; + } + + if (!extended) + return false; + + extended = false; + + if (likely((data & 0x7f) != 0x3b)) { + serio_interrupt(port, 0xe0, 0); + return false; + } + + if (data & 0x80) { + input_report_key(slidebar_input_dev, BTN_TOUCH, 0); + } else { + input_report_key(slidebar_input_dev, BTN_TOUCH, 1); + input_report_abs(slidebar_input_dev, ABS_X, slidebar_pos_get()); + } + input_sync(slidebar_input_dev); + + return true; +} + +static ssize_t show_slidebar_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", slidebar_mode_get()); +} + +static ssize_t store_slidebar_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 mode; + int error; + + error = kstrtou8(buf, 0, &mode); + if (error) + return error; + + slidebar_mode_set(mode); + + return count; +} + +static DEVICE_ATTR(slidebar_mode, S_IWUSR | S_IRUGO, + show_slidebar_mode, store_slidebar_mode); + +static struct attribute *ideapad_attrs[] = { + &dev_attr_slidebar_mode.attr, + NULL +}; + +static struct attribute_group ideapad_attr_group = { + .attrs = ideapad_attrs +}; + +static const struct attribute_group *ideapad_attr_groups[] = { + &ideapad_attr_group, + NULL +}; + +static int __init ideapad_probe(struct platform_device* pdev) +{ + int err; + + if (!request_region(IDEAPAD_BASE, 3, "ideapad_slidebar")) { + dev_err(&pdev->dev, "IO ports are busy\n"); + return -EBUSY; + } + + slidebar_input_dev = input_allocate_device(); + if (!slidebar_input_dev) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + err = -ENOMEM; + goto err_release_ports; + } + + slidebar_input_dev->name = "IdeaPad Slidebar"; + slidebar_input_dev->id.bustype = BUS_HOST; + slidebar_input_dev->dev.parent = &pdev->dev; + input_set_capability(slidebar_input_dev, EV_KEY, BTN_TOUCH); + 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); + if (err) { + dev_err(&pdev->dev, + "Failed to install i8042 filter: %d\n", err); + goto err_free_dev; + } + + err = input_register_device(slidebar_input_dev); + if (err) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", err); + goto err_remove_filter; + } + + return 0; + +err_remove_filter: + i8042_remove_filter(slidebar_i8042_filter); +err_free_dev: + input_free_device(slidebar_input_dev); +err_release_ports: + release_region(IDEAPAD_BASE, 3); + return err; +} + +static int 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 = { + .driver = { + .name = "ideapad_slidebar", + .owner = THIS_MODULE, + }, + .remove = ideapad_remove, +}; + +static int __init ideapad_dmi_check(const struct dmi_system_id *id) +{ + pr_info("Laptop model '%s'\n", id->ident); + return 1; +} + +static const struct dmi_system_id ideapad_dmi[] __initconst = { + { + .ident = "Lenovo IdeaPad Y550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20017"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550") + }, + .callback = ideapad_dmi_check + }, + { + .ident = "Lenovo IdeaPad Y550P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20035"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550P") + }, + .callback = ideapad_dmi_check + }, + { NULL, } +}; +MODULE_DEVICE_TABLE(dmi, ideapad_dmi); + +static int __init slidebar_init(void) +{ + int err; + + if (!force && !dmi_check_system(ideapad_dmi)) { + pr_err("DMI does not match\n"); + return -ENODEV; + } + + slidebar_platform_dev = platform_device_alloc("ideapad_slidebar", -1); + if (!slidebar_platform_dev) { + pr_err("Not enough memory\n"); + return -ENOMEM; + } + + slidebar_platform_dev->dev.groups = ideapad_attr_groups; + + err = platform_device_add(slidebar_platform_dev); + if (err) { + pr_err("Failed to register platform device\n"); + goto err_free_dev; + } + + err = platform_driver_probe(&slidebar_drv, ideapad_probe); + if (err) { + pr_err("Failed to register platform driver\n"); + goto err_delete_dev; + } + + return 0; + +err_delete_dev: + platform_device_del(slidebar_platform_dev); +err_free_dev: + platform_device_put(slidebar_platform_dev); + return err; +} + +static void __exit slidebar_exit(void) +{ + platform_device_unregister(slidebar_platform_dev); + platform_driver_unregister(&slidebar_drv); +} + +module_init(slidebar_init); +module_exit(slidebar_exit); + +MODULE_AUTHOR("Andrey Moiseev <o2g.org.ru@gmail.com>"); +MODULE_DESCRIPTION("Slidebar input support for some Lenovo IdeaPad laptops"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c new file mode 100644 index 00000000000..719410feb84 --- /dev/null +++ b/drivers/input/misc/ims-pcu.c @@ -0,0 +1,2144 @@ +/* + * 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> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/ihex.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/usb/input.h> +#include <linux/usb/cdc.h> +#include <asm/unaligned.h> + +#define IMS_PCU_KEYMAP_LEN 32 + +struct ims_pcu_buttons { + struct input_dev *input; + char name[32]; + char phys[32]; + unsigned short keymap[IMS_PCU_KEYMAP_LEN]; +}; + +struct ims_pcu_gamepad { + struct input_dev *input; + char name[32]; + char phys[32]; +}; + +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_BL_RESET_REASON_LEN (2 + 1) + +#define IMS_PCU_PCU_B_DEVICE_ID 5 + +#define IMS_PCU_BUF_SIZE 128 + +struct ims_pcu { + struct usb_device *udev; + struct device *dev; /* control interface's device, used for logging */ + + unsigned int device_no; + + bool bootloader_mode; + + char part_number[IMS_PCU_PART_NUMBER_LEN]; + char serial_number[IMS_PCU_SERIAL_NUMBER_LEN]; + char date_of_manufacturing[IMS_PCU_DOM_LEN]; + char fw_version[IMS_PCU_FW_VERSION_LEN]; + char bl_version[IMS_PCU_BL_VERSION_LEN]; + char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; + int update_firmware_status; + u8 device_id; + + u8 ofn_reg_addr; + + struct usb_interface *ctrl_intf; + + struct usb_endpoint_descriptor *ep_ctrl; + struct urb *urb_ctrl; + u8 *urb_ctrl_buf; + dma_addr_t ctrl_dma; + size_t max_ctrl_size; + + struct usb_interface *data_intf; + + struct usb_endpoint_descriptor *ep_in; + struct urb *urb_in; + u8 *urb_in_buf; + dma_addr_t read_dma; + size_t max_in_size; + + struct usb_endpoint_descriptor *ep_out; + u8 *urb_out_buf; + size_t max_out_size; + + u8 read_buf[IMS_PCU_BUF_SIZE]; + u8 read_pos; + u8 check_sum; + bool have_stx; + bool have_dle; + + u8 cmd_buf[IMS_PCU_BUF_SIZE]; + u8 ack_id; + u8 expected_response; + u8 cmd_buf_len; + struct completion cmd_done; + struct mutex cmd_mutex; + + u32 fw_start_addr; + u32 fw_end_addr; + struct completion async_firmware_done; + + struct ims_pcu_buttons buttons; + struct ims_pcu_gamepad *gamepad; + struct ims_pcu_backlight backlight; + + bool setup_complete; /* Input and LED devices have been created */ +}; + + +/********************************************************************* + * Buttons Input device support * + *********************************************************************/ + +static const unsigned short ims_pcu_keymap_1[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, +}; + +static const unsigned short ims_pcu_keymap_2[] = { + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, +}; + +static const unsigned short ims_pcu_keymap_3[] = { + [1] = KEY_HOMEPAGE, + [2] = KEY_ATTENDANT_TOGGLE, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_DISPLAYTOGGLE, + [18] = KEY_PLAYPAUSE, +}; + +static const unsigned short ims_pcu_keymap_4[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, + [18] = KEY_PLAYPAUSE, +}; + +static const unsigned short ims_pcu_keymap_5[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, +}; + +struct ims_pcu_device_info { + const unsigned short *keymap; + size_t keymap_len; + bool has_gamepad; +}; + +#define IMS_PCU_DEVINFO(_n, _gamepad) \ + [_n] = { \ + .keymap = ims_pcu_keymap_##_n, \ + .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \ + .has_gamepad = _gamepad, \ + } + +static const struct ims_pcu_device_info ims_pcu_device_info[] = { + IMS_PCU_DEVINFO(1, true), + IMS_PCU_DEVINFO(2, true), + IMS_PCU_DEVINFO(3, true), + IMS_PCU_DEVINFO(4, true), + IMS_PCU_DEVINFO(5, false), +}; + +static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + struct input_dev *input = buttons->input; + int i; + + for (i = 0; i < 32; i++) { + unsigned short keycode = buttons->keymap[i]; + + if (keycode != KEY_RESERVED) + input_report_key(input, keycode, data & (1UL << i)); + } + + input_sync(input); +} + +static int ims_pcu_setup_buttons(struct ims_pcu *pcu, + const unsigned short *keymap, + size_t keymap_len) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + struct input_dev *input; + int i; + int error; + + input = input_allocate_device(); + if (!input) { + dev_err(pcu->dev, + "Not enough memory for input input device\n"); + return -ENOMEM; + } + + snprintf(buttons->name, sizeof(buttons->name), + "IMS PCU#%d Button Interface", pcu->device_no); + + usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys)); + strlcat(buttons->phys, "/input0", sizeof(buttons->phys)); + + memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len); + + input->name = buttons->name; + input->phys = buttons->phys; + usb_to_input_id(pcu->udev, &input->id); + input->dev.parent = &pcu->ctrl_intf->dev; + + input->keycode = buttons->keymap; + input->keycodemax = ARRAY_SIZE(buttons->keymap); + input->keycodesize = sizeof(buttons->keymap[0]); + + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++) + __set_bit(buttons->keymap[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(pcu->dev, + "Failed to register buttons input device: %d\n", + error); + input_free_device(input); + return error; + } + + buttons->input = input; + return 0; +} + +static void ims_pcu_destroy_buttons(struct ims_pcu *pcu) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + + input_unregister_device(buttons->input); +} + + +/********************************************************************* + * Gamepad Input device support * + *********************************************************************/ + +static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data) +{ + struct ims_pcu_gamepad *gamepad = pcu->gamepad; + struct input_dev *input = gamepad->input; + int x, y; + + x = !!(data & (1 << 14)) - !!(data & (1 << 13)); + y = !!(data & (1 << 12)) - !!(data & (1 << 11)); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + + input_report_key(input, BTN_A, data & (1 << 7)); + input_report_key(input, BTN_B, data & (1 << 8)); + input_report_key(input, BTN_X, data & (1 << 9)); + input_report_key(input, BTN_Y, data & (1 << 10)); + input_report_key(input, BTN_START, data & (1 << 15)); + input_report_key(input, BTN_SELECT, data & (1 << 16)); + + input_sync(input); +} + +static int ims_pcu_setup_gamepad(struct ims_pcu *pcu) +{ + struct ims_pcu_gamepad *gamepad; + struct input_dev *input; + int error; + + gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL); + input = input_allocate_device(); + if (!gamepad || !input) { + dev_err(pcu->dev, + "Not enough memory for gamepad device\n"); + error = -ENOMEM; + goto err_free_mem; + } + + gamepad->input = input; + + snprintf(gamepad->name, sizeof(gamepad->name), + "IMS PCU#%d Gamepad Interface", pcu->device_no); + + usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys)); + strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys)); + + input->name = gamepad->name; + input->phys = gamepad->phys; + usb_to_input_id(pcu->udev, &input->id); + input->dev.parent = &pcu->ctrl_intf->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_A, input->keybit); + __set_bit(BTN_B, input->keybit); + __set_bit(BTN_X, input->keybit); + __set_bit(BTN_Y, input->keybit); + __set_bit(BTN_START, input->keybit); + __set_bit(BTN_SELECT, input->keybit); + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_X, -1, 1, 0, 0); + input_set_abs_params(input, ABS_Y, -1, 1, 0, 0); + + error = input_register_device(input); + if (error) { + dev_err(pcu->dev, + "Failed to register gamepad input device: %d\n", + error); + goto err_free_mem; + } + + pcu->gamepad = gamepad; + return 0; + +err_free_mem: + input_free_device(input); + kfree(gamepad); + return -ENOMEM; +} + +static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) +{ + struct ims_pcu_gamepad *gamepad = pcu->gamepad; + + input_unregister_device(gamepad->input); + kfree(gamepad); +} + + +/********************************************************************* + * PCU Communication protocol handling * + *********************************************************************/ + +#define IMS_PCU_PROTOCOL_STX 0x02 +#define IMS_PCU_PROTOCOL_ETX 0x03 +#define IMS_PCU_PROTOCOL_DLE 0x10 + +/* PCU commands */ +#define IMS_PCU_CMD_STATUS 0xa0 +#define IMS_PCU_CMD_PCU_RESET 0xa1 +#define IMS_PCU_CMD_RESET_REASON 0xa2 +#define IMS_PCU_CMD_SEND_BUTTONS 0xa3 +#define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4 +#define IMS_PCU_CMD_GET_INFO 0xa5 +#define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6 +#define IMS_PCU_CMD_EEPROM 0xa7 +#define IMS_PCU_CMD_GET_FW_VERSION 0xa8 +#define IMS_PCU_CMD_GET_BL_VERSION 0xa9 +#define IMS_PCU_CMD_SET_INFO 0xab +#define IMS_PCU_CMD_GET_BRIGHTNESS 0xac +#define IMS_PCU_CMD_GET_DEVICE_ID 0xae +#define IMS_PCU_CMD_SPECIAL_INFO 0xb0 +#define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */ +#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3 +#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 + +/* PCU responses */ +#define IMS_PCU_RSP_STATUS 0xc0 +#define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */ +#define IMS_PCU_RSP_RESET_REASON 0xc2 +#define IMS_PCU_RSP_SEND_BUTTONS 0xc3 +#define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */ +#define IMS_PCU_RSP_GET_INFO 0xc5 +#define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6 +#define IMS_PCU_RSP_EEPROM 0xc7 +#define IMS_PCU_RSP_GET_FW_VERSION 0xc8 +#define IMS_PCU_RSP_GET_BL_VERSION 0xc9 +#define IMS_PCU_RSP_SET_INFO 0xcb +#define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc +#define IMS_PCU_RSP_CMD_INVALID 0xcd +#define IMS_PCU_RSP_GET_DEVICE_ID 0xce +#define IMS_PCU_RSP_SPECIAL_INFO 0xd0 +#define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */ +#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2 +#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 + + +#define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ +#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ + + +#define IMS_PCU_MIN_PACKET_LEN 3 +#define IMS_PCU_DATA_OFFSET 2 + +#define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */ +#define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */ + +static void ims_pcu_report_events(struct ims_pcu *pcu) +{ + u32 data = get_unaligned_be32(&pcu->read_buf[3]); + + ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); + if (pcu->gamepad) + ims_pcu_gamepad_report(pcu, data); +} + +static void ims_pcu_handle_response(struct ims_pcu *pcu) +{ + switch (pcu->read_buf[0]) { + case IMS_PCU_RSP_EVNT_BUTTONS: + if (likely(pcu->setup_complete)) + ims_pcu_report_events(pcu); + break; + + default: + /* + * See if we got command completion. + * If both the sequence and response code match save + * the data and signal completion. + */ + if (pcu->read_buf[0] == pcu->expected_response && + pcu->read_buf[1] == pcu->ack_id - 1) { + + memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos); + pcu->cmd_buf_len = pcu->read_pos; + complete(&pcu->cmd_done); + } + break; + } +} + +static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) +{ + int i; + + for (i = 0; i < urb->actual_length; i++) { + u8 data = pcu->urb_in_buf[i]; + + /* Skip everything until we get Start Xmit */ + if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX) + continue; + + if (pcu->have_dle) { + pcu->have_dle = false; + pcu->read_buf[pcu->read_pos++] = data; + pcu->check_sum += data; + continue; + } + + switch (data) { + case IMS_PCU_PROTOCOL_STX: + if (pcu->have_stx) + dev_warn(pcu->dev, + "Unexpected STX at byte %d, discarding old data\n", + pcu->read_pos); + pcu->have_stx = true; + pcu->have_dle = false; + pcu->read_pos = 0; + pcu->check_sum = 0; + break; + + case IMS_PCU_PROTOCOL_DLE: + pcu->have_dle = true; + break; + + case IMS_PCU_PROTOCOL_ETX: + if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) { + dev_warn(pcu->dev, + "Short packet received (%d bytes), ignoring\n", + pcu->read_pos); + } else if (pcu->check_sum != 0) { + dev_warn(pcu->dev, + "Invalid checksum in packet (%d bytes), ignoring\n", + pcu->read_pos); + } else { + ims_pcu_handle_response(pcu); + } + + pcu->have_stx = false; + pcu->have_dle = false; + pcu->read_pos = 0; + break; + + default: + pcu->read_buf[pcu->read_pos++] = data; + pcu->check_sum += data; + break; + } + } +} + +static bool ims_pcu_byte_needs_escape(u8 byte) +{ + return byte == IMS_PCU_PROTOCOL_STX || + byte == IMS_PCU_PROTOCOL_ETX || + byte == IMS_PCU_PROTOCOL_DLE; +} + +static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu, + u8 command, int chunk, int len) +{ + int error; + + error = usb_bulk_msg(pcu->udev, + usb_sndbulkpipe(pcu->udev, + pcu->ep_out->bEndpointAddress), + pcu->urb_out_buf, len, + NULL, IMS_PCU_CMD_WRITE_TIMEOUT); + if (error < 0) { + dev_dbg(pcu->dev, + "Sending 0x%02x command failed at chunk %d: %d\n", + command, chunk, error); + return error; + } + + return 0; +} + +static int ims_pcu_send_command(struct ims_pcu *pcu, + u8 command, const u8 *data, int len) +{ + int count = 0; + int chunk = 0; + int delta; + int i; + int error; + u8 csum = 0; + u8 ack_id; + + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX; + + /* We know the command need not be escaped */ + pcu->urb_out_buf[count++] = command; + csum += command; + + ack_id = pcu->ack_id++; + if (ack_id == 0xff) + ack_id = pcu->ack_id++; + + if (ims_pcu_byte_needs_escape(ack_id)) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = ack_id; + csum += ack_id; + + for (i = 0; i < len; i++) { + + delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1; + if (count + delta >= pcu->max_out_size) { + error = ims_pcu_send_cmd_chunk(pcu, command, + ++chunk, count); + if (error) + return error; + + count = 0; + } + + if (delta == 2) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = data[i]; + csum += data[i]; + } + + csum = 1 + ~csum; + + delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2; + if (count + delta >= pcu->max_out_size) { + error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count); + if (error) + return error; + + count = 0; + } + + if (delta == 3) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = csum; + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX; + + return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count); +} + +static int __ims_pcu_execute_command(struct ims_pcu *pcu, + u8 command, const void *data, size_t len, + u8 expected_response, int response_time) +{ + int error; + + pcu->expected_response = expected_response; + init_completion(&pcu->cmd_done); + + error = ims_pcu_send_command(pcu, command, data, len); + if (error) + return error; + + if (expected_response && + !wait_for_completion_timeout(&pcu->cmd_done, + msecs_to_jiffies(response_time))) { + dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command); + return -ETIMEDOUT; + } + + return 0; +} + +#define ims_pcu_execute_command(pcu, code, data, len) \ + __ims_pcu_execute_command(pcu, \ + IMS_PCU_CMD_##code, data, len, \ + IMS_PCU_RSP_##code, \ + IMS_PCU_CMD_RESPONSE_TIMEOUT) + +#define ims_pcu_execute_query(pcu, code) \ + ims_pcu_execute_command(pcu, code, NULL, 0) + +/* Bootloader commands */ +#define IMS_PCU_BL_CMD_QUERY_DEVICE 0xa1 +#define IMS_PCU_BL_CMD_UNLOCK_CONFIG 0xa2 +#define IMS_PCU_BL_CMD_ERASE_APP 0xa3 +#define IMS_PCU_BL_CMD_PROGRAM_DEVICE 0xa4 +#define IMS_PCU_BL_CMD_PROGRAM_COMPLETE 0xa5 +#define IMS_PCU_BL_CMD_READ_APP 0xa6 +#define IMS_PCU_BL_CMD_RESET_DEVICE 0xa7 +#define IMS_PCU_BL_CMD_LAUNCH_APP 0xa8 + +/* Bootloader commands */ +#define IMS_PCU_BL_RSP_QUERY_DEVICE 0xc1 +#define IMS_PCU_BL_RSP_UNLOCK_CONFIG 0xc2 +#define IMS_PCU_BL_RSP_ERASE_APP 0xc3 +#define IMS_PCU_BL_RSP_PROGRAM_DEVICE 0xc4 +#define IMS_PCU_BL_RSP_PROGRAM_COMPLETE 0xc5 +#define IMS_PCU_BL_RSP_READ_APP 0xc6 +#define IMS_PCU_BL_RSP_RESET_DEVICE 0 /* originally 0xa7 */ +#define IMS_PCU_BL_RSP_LAUNCH_APP 0 /* originally 0xa8 */ + +#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) +{ + int error; + + pcu->cmd_buf[0] = command; + if (data) + memcpy(&pcu->cmd_buf[1], data, len); + + error = __ims_pcu_execute_command(pcu, + IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1, + expected_response ? IMS_PCU_RSP_BOOTLOADER : 0, + response_time); + if (error) { + dev_err(pcu->dev, + "Failure when sending 0x%02x command to bootloader, error: %d\n", + pcu->cmd_buf[0], error); + return error; + } + + if (expected_response && pcu->cmd_buf[2] != expected_response) { + dev_err(pcu->dev, + "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", + pcu->cmd_buf[2], expected_response); + return -EINVAL; + } + + return 0; +} + +#define ims_pcu_execute_bl_command(pcu, code, data, len, timeout) \ + __ims_pcu_execute_bl_command(pcu, \ + IMS_PCU_BL_CMD_##code, data, len, \ + IMS_PCU_BL_RSP_##code, timeout) \ + +#define IMS_PCU_INFO_PART_OFFSET 2 +#define IMS_PCU_INFO_DOM_OFFSET 17 +#define IMS_PCU_INFO_SERIAL_OFFSET 25 + +#define IMS_PCU_SET_INFO_SIZE 31 + +static int ims_pcu_get_info(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_execute_query(pcu, GET_INFO); + if (error) { + dev_err(pcu->dev, + "GET_INFO command failed, error: %d\n", error); + return error; + } + + memcpy(pcu->part_number, + &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], + sizeof(pcu->part_number)); + memcpy(pcu->date_of_manufacturing, + &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], + sizeof(pcu->date_of_manufacturing)); + memcpy(pcu->serial_number, + &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], + sizeof(pcu->serial_number)); + + return 0; +} + +static int ims_pcu_set_info(struct ims_pcu *pcu) +{ + int error; + + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], + pcu->part_number, sizeof(pcu->part_number)); + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], + pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing)); + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], + pcu->serial_number, sizeof(pcu->serial_number)); + + error = ims_pcu_execute_command(pcu, SET_INFO, + &pcu->cmd_buf[IMS_PCU_DATA_OFFSET], + IMS_PCU_SET_INFO_SIZE); + if (error) { + dev_err(pcu->dev, + "Failed to update device information, error: %d\n", + error); + return error; + } + + return 0; +} + +static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu) +{ + int error; + + /* Execute jump to the bootoloader */ + 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", + error); + return error; + } + + return 0; +} + +/********************************************************************* + * Firmware Update handling * + *********************************************************************/ + +#define IMS_PCU_FIRMWARE_NAME "imspcu.fw" + +struct ims_pcu_flash_fmt { + __le32 addr; + u8 len; + u8 data[]; +}; + +static unsigned int ims_pcu_count_fw_records(const struct firmware *fw) +{ + const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; + unsigned int count = 0; + + while (rec) { + count++; + rec = ihex_next_binrec(rec); + } + + return count; +} + +static int ims_pcu_verify_block(struct ims_pcu *pcu, + u32 addr, u8 len, const u8 *data) +{ + struct ims_pcu_flash_fmt *fragment; + int error; + + fragment = (void *)&pcu->cmd_buf[1]; + put_unaligned_le32(addr, &fragment->addr); + fragment->len = len; + + error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, + "Failed to retrieve block at 0x%08x, len %d, error: %d\n", + addr, len, error); + return error; + } + + fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; + if (get_unaligned_le32(&fragment->addr) != addr || + fragment->len != len) { + dev_err(pcu->dev, + "Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n", + addr, get_unaligned_le32(&fragment->addr), + len, fragment->len); + return -EINVAL; + } + + if (memcmp(fragment->data, data, len)) { + dev_err(pcu->dev, + "Mismatch in block at 0x%08x, len %d\n", + addr, len); + return -EINVAL; + } + + return 0; +} + +static int ims_pcu_flash_firmware(struct ims_pcu *pcu, + const struct firmware *fw, + unsigned int n_fw_records) +{ + const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; + struct ims_pcu_flash_fmt *fragment; + unsigned int count = 0; + u32 addr; + u8 len; + int error; + + error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000); + if (error) { + dev_err(pcu->dev, + "Failed to erase application image, error: %d\n", + error); + return error; + } + + while (rec) { + /* + * The firmware format is messed up for some reason. + * The address twice that of what is needed for some + * reason and we end up overwriting half of the data + * with the next record. + */ + addr = be32_to_cpu(rec->addr) / 2; + len = be16_to_cpu(rec->len); + + fragment = (void *)&pcu->cmd_buf[1]; + put_unaligned_le32(addr, &fragment->addr); + fragment->len = len; + memcpy(fragment->data, rec->data, len); + + error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE, + NULL, len + 5, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, + "Failed to write block at 0x%08x, len %d, error: %d\n", + addr, len, error); + return error; + } + + if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) { + error = ims_pcu_verify_block(pcu, addr, len, rec->data); + if (error) + return error; + } + + count++; + pcu->update_firmware_status = (count * 100) / n_fw_records; + + rec = ihex_next_binrec(rec); + } + + error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE, + NULL, 0, 2000); + if (error) + dev_err(pcu->dev, + "Failed to send PROGRAM_COMPLETE, error: %d\n", + error); + + return 0; +} + +static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, + const struct firmware *fw) +{ + unsigned int n_fw_records; + int retval; + + dev_info(pcu->dev, "Updating firmware %s, size: %zu\n", + IMS_PCU_FIRMWARE_NAME, fw->size); + + n_fw_records = ims_pcu_count_fw_records(fw); + + retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records); + if (retval) + goto out; + + retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0); + if (retval) + dev_err(pcu->dev, + "Failed to start application image, error: %d\n", + retval); + +out: + pcu->update_firmware_status = retval; + sysfs_notify(&pcu->dev->kobj, NULL, "update_firmware_status"); + return retval; +} + +static void ims_pcu_process_async_firmware(const struct firmware *fw, + void *context) +{ + struct ims_pcu *pcu = context; + int error; + + if (!fw) { + dev_err(pcu->dev, "Failed to get firmware %s\n", + IMS_PCU_FIRMWARE_NAME); + goto out; + } + + error = ihex_validate_fw(fw); + if (error) { + dev_err(pcu->dev, "Firmware %s is invalid\n", + IMS_PCU_FIRMWARE_NAME); + goto out; + } + + mutex_lock(&pcu->cmd_mutex); + ims_pcu_handle_firmware_update(pcu, fw); + mutex_unlock(&pcu->cmd_mutex); + + release_firmware(fw); + +out: + complete(&pcu->async_firmware_done); +} + +/********************************************************************* + * Backlight LED device support * + *********************************************************************/ + +#define IMS_PCU_MAX_BRIGHTNESS 31998 + +static void ims_pcu_backlight_work(struct work_struct *work) +{ + struct ims_pcu_backlight *backlight = + container_of(work, struct ims_pcu_backlight, work); + 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); + int error; + + mutex_lock(&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); + + backlight->desired_brightness = value; + schedule_work(&backlight->work); +} + +static enum led_brightness +ims_pcu_backlight_get_brightness(struct led_classdev *cdev) +{ + struct ims_pcu_backlight *backlight = + container_of(cdev, struct ims_pcu_backlight, cdev); + struct ims_pcu *pcu = + container_of(backlight, struct ims_pcu, backlight); + int brightness; + int error; + + mutex_lock(&pcu->cmd_mutex); + + error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS); + if (error) { + dev_warn(pcu->dev, + "Failed to get current brightness, error: %d\n", + error); + /* Assume the LED is OFF */ + brightness = LED_OFF; + } else { + brightness = + get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); + } + + mutex_unlock(&pcu->cmd_mutex); + + return brightness; +} + +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; + + error = led_classdev_register(pcu->dev, &backlight->cdev); + if (error) { + dev_err(pcu->dev, + "Failed to register backlight LED device, error: %d\n", + error); + return error; + } + + return 0; +} + +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); +} + + +/********************************************************************* + * Sysfs attributes handling * + *********************************************************************/ + +struct ims_pcu_attribute { + struct device_attribute dattr; + size_t field_offset; + int field_length; +}; + +static ssize_t ims_pcu_attribute_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_attribute *attr = + 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); +} + +static ssize_t ims_pcu_attribute_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_attribute *attr = + container_of(dattr, struct ims_pcu_attribute, dattr); + char *field = (char *)pcu + attr->field_offset; + size_t data_len; + int error; + + if (count > attr->field_length) + return -EINVAL; + + data_len = strnlen(buf, attr->field_length); + 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); + + 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); + + mutex_unlock(&pcu->cmd_mutex); + + return error < 0 ? error : count; +} + +#define IMS_PCU_ATTR(_field, _mode) \ +struct ims_pcu_attribute ims_pcu_attr_##_field = { \ + .dattr = __ATTR(_field, _mode, \ + ims_pcu_attribute_show, \ + ims_pcu_attribute_store), \ + .field_offset = offsetof(struct ims_pcu, _field), \ + .field_length = sizeof(((struct ims_pcu *)NULL)->_field), \ +} + +#define IMS_PCU_RO_ATTR(_field) \ + IMS_PCU_ATTR(_field, S_IRUGO) +#define IMS_PCU_RW_ATTR(_field) \ + IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR) + +static IMS_PCU_RW_ATTR(part_number); +static IMS_PCU_RW_ATTR(serial_number); +static IMS_PCU_RW_ATTR(date_of_manufacturing); + +static IMS_PCU_RO_ATTR(fw_version); +static IMS_PCU_RO_ATTR(bl_version); +static IMS_PCU_RO_ATTR(reset_reason); + +static ssize_t ims_pcu_reset_device(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + static const u8 reset_byte = 1; + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int value; + int error; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value != 1) + return -EINVAL; + + dev_info(pcu->dev, "Attempting to reset device\n"); + + error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); + if (error) { + dev_info(pcu->dev, + "Failed to reset device, error: %d\n", + error); + return error; + } + + return count; +} + +static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device); + +static ssize_t ims_pcu_update_firmware_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + 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; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value != 1) + return -EINVAL; + + error = mutex_lock_interruptible(&pcu->cmd_mutex); + if (error) + return error; + + 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; + } + + /* + * 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); + +out: + mutex_unlock(&pcu->cmd_mutex); + return error ?: count; +} + +static DEVICE_ATTR(update_firmware, S_IWUSR, + NULL, ims_pcu_update_firmware_store); + +static ssize_t +ims_pcu_update_firmware_status_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + 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); +} + +static DEVICE_ATTR(update_firmware_status, S_IRUGO, + ims_pcu_update_firmware_status_show, NULL); + +static struct attribute *ims_pcu_attrs[] = { + &ims_pcu_attr_part_number.dattr.attr, + &ims_pcu_attr_serial_number.dattr.attr, + &ims_pcu_attr_date_of_manufacturing.dattr.attr, + &ims_pcu_attr_fw_version.dattr.attr, + &ims_pcu_attr_bl_version.dattr.attr, + &ims_pcu_attr_reset_reason.dattr.attr, + &dev_attr_reset_device.attr, + &dev_attr_update_firmware.attr, + &dev_attr_update_firmware_status.attr, + NULL +}; + +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 usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + umode_t mode = attr->mode; + + if (pcu->bootloader_mode) { + if (attr != &dev_attr_update_firmware_status.attr && + attr != &dev_attr_update_firmware.attr && + attr != &dev_attr_reset_device.attr) { + mode = 0; + } + } else { + if (attr == &dev_attr_update_firmware_status.attr) + mode = 0; + } + + return mode; +} + +static struct attribute_group ims_pcu_attr_group = { + .is_visible = ims_pcu_is_attr_visible, + .attrs = ims_pcu_attrs, +}; + +/* Support for a separate OFN attribute group */ + +#define OFN_REG_RESULT_OFFSET 2 + +static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) +{ + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG, + &addr, sizeof(addr)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + /* We only need LSB */ + *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET]; + return 0; +} + +static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) +{ + u8 buffer[] = { addr, data }; + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG, + &buffer, sizeof(buffer)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + return 0; +} + +static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + 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; + + return scnprintf(buf, PAGE_SIZE, "%x\n", data); +} + +static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value); + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store); + +static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + 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); + + return error; +} + +static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + pcu->ofn_reg_addr = value; + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store); + +struct ims_pcu_ofn_bit_attribute { + struct device_attribute dattr; + u8 addr; + u8 nr; +}; + +static ssize_t ims_pcu_ofn_bit_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + 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; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr))); +} + +static ssize_t ims_pcu_ofn_bit_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + int error; + int value; + u8 data; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value > 1) + return -EINVAL; + + mutex_lock(&pcu->cmd_mutex); + + 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); + } + + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \ +struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \ + .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \ + ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \ + .addr = _addr, \ + .nr = _nr, \ +} + +static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7); +static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6); +static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5); +static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4); +static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1); + +static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6); +static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7); + +static struct attribute *ims_pcu_ofn_attrs[] = { + &dev_attr_reg_data.attr, + &dev_attr_reg_addr.attr, + &ims_pcu_ofn_attr_engine_enable.dattr.attr, + &ims_pcu_ofn_attr_speed_enable.dattr.attr, + &ims_pcu_ofn_attr_assert_enable.dattr.attr, + &ims_pcu_ofn_attr_xyquant_enable.dattr.attr, + &ims_pcu_ofn_attr_xyscale_enable.dattr.attr, + &ims_pcu_ofn_attr_scale_x2.dattr.attr, + &ims_pcu_ofn_attr_scale_y2.dattr.attr, + NULL +}; + +static struct attribute_group ims_pcu_ofn_attr_group = { + .name = "ofn", + .attrs = ims_pcu_ofn_attrs, +}; + +static void ims_pcu_irq(struct urb *urb) +{ + struct ims_pcu *pcu = urb->context; + int retval, status; + + status = urb->status; + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n", + __func__, status); + return; + default: + dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n", + __func__, status); + goto exit; + } + + dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__, + urb->actual_length, urb->actual_length, pcu->urb_in_buf); + + if (urb == pcu->urb_in) + ims_pcu_process_data(pcu, urb); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval && retval != -ENODEV) + dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n", + __func__, retval); +} + +static int ims_pcu_buffers_alloc(struct ims_pcu *pcu) +{ + int error; + + pcu->urb_in_buf = usb_alloc_coherent(pcu->udev, pcu->max_in_size, + GFP_KERNEL, &pcu->read_dma); + if (!pcu->urb_in_buf) { + dev_err(pcu->dev, + "Failed to allocate memory for read buffer\n"); + return -ENOMEM; + } + + pcu->urb_in = usb_alloc_urb(0, GFP_KERNEL); + if (!pcu->urb_in) { + dev_err(pcu->dev, "Failed to allocate input URB\n"); + error = -ENOMEM; + goto err_free_urb_in_buf; + } + + pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + pcu->urb_in->transfer_dma = pcu->read_dma; + + usb_fill_bulk_urb(pcu->urb_in, pcu->udev, + usb_rcvbulkpipe(pcu->udev, + pcu->ep_in->bEndpointAddress), + pcu->urb_in_buf, pcu->max_in_size, + ims_pcu_irq, pcu); + + /* + * We are using usb_bulk_msg() for sending so there is no point + * in allocating memory with usb_alloc_coherent(). + */ + pcu->urb_out_buf = kmalloc(pcu->max_out_size, GFP_KERNEL); + if (!pcu->urb_out_buf) { + dev_err(pcu->dev, "Failed to allocate memory for write buffer\n"); + error = -ENOMEM; + goto err_free_in_urb; + } + + pcu->urb_ctrl_buf = usb_alloc_coherent(pcu->udev, pcu->max_ctrl_size, + GFP_KERNEL, &pcu->ctrl_dma); + if (!pcu->urb_ctrl_buf) { + dev_err(pcu->dev, + "Failed to allocate memory for read buffer\n"); + error = -ENOMEM; + goto err_free_urb_out_buf; + } + + pcu->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL); + if (!pcu->urb_ctrl) { + dev_err(pcu->dev, "Failed to allocate input URB\n"); + error = -ENOMEM; + goto err_free_urb_ctrl_buf; + } + + pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma; + + usb_fill_int_urb(pcu->urb_ctrl, pcu->udev, + usb_rcvintpipe(pcu->udev, + pcu->ep_ctrl->bEndpointAddress), + pcu->urb_ctrl_buf, pcu->max_ctrl_size, + ims_pcu_irq, pcu, pcu->ep_ctrl->bInterval); + + return 0; + +err_free_urb_ctrl_buf: + usb_free_coherent(pcu->udev, pcu->max_ctrl_size, + pcu->urb_ctrl_buf, pcu->ctrl_dma); +err_free_urb_out_buf: + kfree(pcu->urb_out_buf); +err_free_in_urb: + usb_free_urb(pcu->urb_in); +err_free_urb_in_buf: + usb_free_coherent(pcu->udev, pcu->max_in_size, + pcu->urb_in_buf, pcu->read_dma); + return error; +} + +static void ims_pcu_buffers_free(struct ims_pcu *pcu) +{ + usb_kill_urb(pcu->urb_in); + usb_free_urb(pcu->urb_in); + + usb_free_coherent(pcu->udev, pcu->max_out_size, + pcu->urb_in_buf, pcu->read_dma); + + kfree(pcu->urb_out_buf); + + usb_kill_urb(pcu->urb_ctrl); + usb_free_urb(pcu->urb_ctrl); + + usb_free_coherent(pcu->udev, pcu->max_ctrl_size, + pcu->urb_ctrl_buf, pcu->ctrl_dma); +} + +static const struct usb_cdc_union_desc * +ims_pcu_get_cdc_union_desc(struct usb_interface *intf) +{ + const void *buf = intf->altsetting->extra; + size_t buflen = intf->altsetting->extralen; + struct usb_cdc_union_desc *union_desc; + + if (!buf) { + dev_err(&intf->dev, "Missing descriptor data\n"); + return NULL; + } + + if (!buflen) { + dev_err(&intf->dev, "Zero length descriptor\n"); + return NULL; + } + + while (buflen > 0) { + union_desc = (struct usb_cdc_union_desc *)buf; + + if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE && + union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) { + dev_dbg(&intf->dev, "Found union header\n"); + return union_desc; + } + + buflen -= union_desc->bLength; + buf += union_desc->bLength; + } + + dev_err(&intf->dev, "Missing CDC union descriptor\n"); + return NULL; +} + +static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu) +{ + const struct usb_cdc_union_desc *union_desc; + struct usb_host_interface *alt; + + union_desc = ims_pcu_get_cdc_union_desc(intf); + if (!union_desc) + return -EINVAL; + + pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, + union_desc->bMasterInterface0); + + alt = pcu->ctrl_intf->cur_altsetting; + pcu->ep_ctrl = &alt->endpoint[0].desc; + pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); + + pcu->data_intf = usb_ifnum_to_if(pcu->udev, + union_desc->bSlaveInterface0); + + alt = pcu->data_intf->cur_altsetting; + if (alt->desc.bNumEndpoints != 2) { + dev_err(pcu->dev, + "Incorrect number of endpoints on data interface (%d)\n", + alt->desc.bNumEndpoints); + return -EINVAL; + } + + pcu->ep_out = &alt->endpoint[0].desc; + if (!usb_endpoint_is_bulk_out(pcu->ep_out)) { + dev_err(pcu->dev, + "First endpoint on data interface is not BULK OUT\n"); + return -EINVAL; + } + + pcu->max_out_size = usb_endpoint_maxp(pcu->ep_out); + if (pcu->max_out_size < 8) { + dev_err(pcu->dev, + "Max OUT packet size is too small (%zd)\n", + pcu->max_out_size); + return -EINVAL; + } + + pcu->ep_in = &alt->endpoint[1].desc; + if (!usb_endpoint_is_bulk_in(pcu->ep_in)) { + dev_err(pcu->dev, + "Second endpoint on data interface is not BULK IN\n"); + return -EINVAL; + } + + pcu->max_in_size = usb_endpoint_maxp(pcu->ep_in); + if (pcu->max_in_size < 8) { + dev_err(pcu->dev, + "Max IN packet size is too small (%zd)\n", + pcu->max_in_size); + return -EINVAL; + } + + return 0; +} + +static int ims_pcu_start_io(struct ims_pcu *pcu) +{ + int error; + + error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL); + if (error) { + dev_err(pcu->dev, + "Failed to start control IO - usb_submit_urb failed with result: %d\n", + error); + return -EIO; + } + + error = usb_submit_urb(pcu->urb_in, GFP_KERNEL); + if (error) { + dev_err(pcu->dev, + "Failed to start IO - usb_submit_urb failed with result: %d\n", + error); + usb_kill_urb(pcu->urb_ctrl); + return -EIO; + } + + return 0; +} + +static void ims_pcu_stop_io(struct ims_pcu *pcu) +{ + usb_kill_urb(pcu->urb_in); + usb_kill_urb(pcu->urb_ctrl); +} + +static int ims_pcu_line_setup(struct ims_pcu *pcu) +{ + struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; + struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; + int error; + + memset(line, 0, sizeof(*line)); + line->dwDTERate = cpu_to_le32(57600); + line->bDataBits = 8; + + error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), + USB_CDC_REQ_SET_LINE_CODING, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, interface->desc.bInterfaceNumber, + line, sizeof(struct usb_cdc_line_coding), + 5000); + if (error < 0) { + dev_err(pcu->dev, "Failed to set line coding, error: %d\n", + error); + return error; + } + + error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), + USB_CDC_REQ_SET_CONTROL_LINE_STATE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x03, interface->desc.bInterfaceNumber, + NULL, 0, 5000); + if (error < 0) { + dev_err(pcu->dev, "Failed to set line state, error: %d\n", + error); + return error; + } + + return 0; +} + +static int ims_pcu_get_device_info(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_get_info(pcu); + if (error) + return error; + + error = ims_pcu_execute_query(pcu, GET_FW_VERSION); + if (error) { + dev_err(pcu->dev, + "GET_FW_VERSION command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->fw_version, sizeof(pcu->fw_version), + "%02d%02d%02d%02d.%c%c", + pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], + pcu->cmd_buf[6], pcu->cmd_buf[7]); + + error = ims_pcu_execute_query(pcu, GET_BL_VERSION); + if (error) { + dev_err(pcu->dev, + "GET_BL_VERSION command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->bl_version, sizeof(pcu->bl_version), + "%02d%02d%02d%02d.%c%c", + pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], + pcu->cmd_buf[6], pcu->cmd_buf[7]); + + error = ims_pcu_execute_query(pcu, RESET_REASON); + if (error) { + dev_err(pcu->dev, + "RESET_REASON command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->reset_reason, sizeof(pcu->reset_reason), + "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); + + dev_dbg(pcu->dev, + "P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n", + pcu->part_number, + pcu->date_of_manufacturing, + pcu->serial_number, + pcu->fw_version, + pcu->bl_version, + pcu->reset_reason); + + return 0; +} + +static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) +{ + int error; + + error = ims_pcu_execute_query(pcu, GET_DEVICE_ID); + if (error) { + dev_err(pcu->dev, + "GET_DEVICE_ID command failed, error: %d\n", error); + return error; + } + + *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; + dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id); + + return 0; +} + +static int ims_pcu_init_application_mode(struct ims_pcu *pcu) +{ + static atomic_t device_no = ATOMIC_INIT(0); + + const struct ims_pcu_device_info *info; + int error; + + error = ims_pcu_get_device_info(pcu); + if (error) { + /* Device does not respond to basic queries, hopeless */ + return error; + } + + error = ims_pcu_identify_type(pcu, &pcu->device_id); + if (error) { + dev_err(pcu->dev, + "Failed to identify device, error: %d\n", error); + /* + * Do not signal error, but do not create input nor + * backlight devices either, let userspace figure this + * out (flash a new firmware?). + */ + return 0; + } + + if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) || + !ims_pcu_device_info[pcu->device_id].keymap) { + dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id); + /* Same as above, punt to userspace */ + return 0; + } + + /* Device appears to be operable, complete initialization */ + pcu->device_no = atomic_inc_return(&device_no) - 1; + + /* + * 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; + + info = &ims_pcu_device_info[pcu->device_id]; + error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len); + if (error) + goto err_destroy_backlight; + + if (info->has_gamepad) { + error = ims_pcu_setup_gamepad(pcu); + if (error) + goto err_destroy_buttons; + } + + pcu->setup_complete = true; + + return 0; + +err_destroy_buttons: + ims_pcu_destroy_buttons(pcu); +err_destroy_backlight: + ims_pcu_destroy_backlight(pcu); + return error; +} + +static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu) +{ + if (pcu->setup_complete) { + pcu->setup_complete = false; + mb(); /* make sure flag setting is not reordered */ + + if (pcu->gamepad) + 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); + } +} + +static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, "Bootloader does not respond, aborting\n"); + return error; + } + + pcu->fw_start_addr = + get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); + pcu->fw_end_addr = + get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]); + + dev_info(pcu->dev, + "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n", + pcu->fw_start_addr, pcu->fw_end_addr); + + error = request_firmware_nowait(THIS_MODULE, true, + IMS_PCU_FIRMWARE_NAME, + pcu->dev, GFP_KERNEL, pcu, + ims_pcu_process_async_firmware); + if (error) { + /* This error is not fatal, let userspace have another chance */ + complete(&pcu->async_firmware_done); + } + + return 0; +} + +static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu) +{ + /* Make sure our initial firmware request has completed */ + wait_for_completion(&pcu->async_firmware_done); +} + +#define IMS_PCU_APPLICATION_MODE 0 +#define IMS_PCU_BOOTLOADER_MODE 1 + +static struct usb_driver ims_pcu_driver; + +static int ims_pcu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ims_pcu *pcu; + int error; + + pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL); + if (!pcu) + return -ENOMEM; + + pcu->dev = &intf->dev; + pcu->udev = udev; + pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE; + mutex_init(&pcu->cmd_mutex); + init_completion(&pcu->cmd_done); + init_completion(&pcu->async_firmware_done); + + error = ims_pcu_parse_cdc_data(intf, pcu); + if (error) + goto err_free_mem; + + error = usb_driver_claim_interface(&ims_pcu_driver, + pcu->data_intf, pcu); + if (error) { + dev_err(&intf->dev, + "Unable to claim corresponding data interface: %d\n", + error); + goto err_free_mem; + } + + usb_set_intfdata(pcu->ctrl_intf, pcu); + usb_set_intfdata(pcu->data_intf, pcu); + + error = ims_pcu_buffers_alloc(pcu); + if (error) + goto err_unclaim_intf; + + error = ims_pcu_start_io(pcu); + if (error) + goto err_free_buffers; + + error = ims_pcu_line_setup(pcu); + 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; + + 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: + ims_pcu_buffers_free(pcu); +err_unclaim_intf: + usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf); +err_free_mem: + kfree(pcu); + return error; +} + +static void ims_pcu_disconnect(struct usb_interface *intf) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + + usb_set_intfdata(intf, NULL); + + /* + * See if we are dealing with control or data interface. The cleanup + * happens when we unbind primary (control) interface. + */ + 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) + ims_pcu_destroy_bootloader_mode(pcu); + else + ims_pcu_destroy_application_mode(pcu); + + ims_pcu_buffers_free(pcu); + kfree(pcu); +} + +#ifdef CONFIG_PM +static int ims_pcu_suspend(struct usb_interface *intf, + pm_message_t message) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + + if (alt->desc.bInterfaceClass == USB_CLASS_COMM) + ims_pcu_stop_io(pcu); + + return 0; +} + +static int ims_pcu_resume(struct usb_interface *intf) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + int retval = 0; + + if (alt->desc.bInterfaceClass == USB_CLASS_COMM) { + retval = ims_pcu_start_io(pcu); + if (retval == 0) + retval = ims_pcu_line_setup(pcu); + } + + return retval; +} +#endif + +static const struct usb_device_id ims_pcu_id_table[] = { + { + USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_V25TER), + .driver_info = IMS_PCU_APPLICATION_MODE, + }, + { + USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_V25TER), + .driver_info = IMS_PCU_BOOTLOADER_MODE, + }, + { } +}; + +static struct usb_driver ims_pcu_driver = { + .name = "ims_pcu", + .id_table = ims_pcu_id_table, + .probe = ims_pcu_probe, + .disconnect = ims_pcu_disconnect, +#ifdef CONFIG_PM + .suspend = ims_pcu_suspend, + .resume = ims_pcu_resume, + .reset_resume = ims_pcu_resume, +#endif +}; + +module_usb_driver(ims_pcu_driver); + +MODULE_DESCRIPTION("IMS Passenger Control Unit driver"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 50e28306830..ed8e5e8449d 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -20,6 +20,7 @@ #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>"); @@ -35,15 +36,12 @@ static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) spin_lock_irqsave(&beep_lock, flags); - if (count) { - gpio_line_config(pin, IXP4XX_GPIO_OUT); - gpio_line_set(pin, IXP4XX_GPIO_LOW); - + if (count) { + gpio_direction_output(pin, 0); *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; } else { - gpio_line_config(pin, IXP4XX_GPIO_IN); - gpio_line_set(pin, IXP4XX_GPIO_HIGH); - + gpio_direction_output(pin, 1); + gpio_direction_input(pin); *IXP4XX_OSRT2 = 0; } @@ -69,7 +67,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned } if (value > 20 && value < 32767) - count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1; + count = (ixp4xx_timer_freq / (value * 4)) - 1; ixp4xx_spkr_control(pin, count); @@ -78,16 +76,18 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned 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 */ - *IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id); + gpio_set_value(pin, !gpio_get_value(pin)); return IRQ_HANDLED; } -static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) +static int ixp4xx_spkr_probe(struct platform_device *dev) { struct input_dev *input_dev; int err; @@ -110,11 +110,15 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) 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_device; + goto err_free_gpio; err = input_register_device(input_dev); if (err) @@ -125,26 +129,28 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) return 0; err_free_irq: - free_irq(IRQ_IXP4XX_TIMER2, dev); + 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 __devexit ixp4xx_spkr_remove(struct platform_device *dev) +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); - platform_set_drvdata(dev, NULL); /* turn the speaker off */ disable_irq(IRQ_IXP4XX_TIMER2); ixp4xx_spkr_control(pin, 0); - free_irq(IRQ_IXP4XX_TIMER2, dev); + free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); + gpio_free(dev->id); return 0; } @@ -165,7 +171,7 @@ static struct platform_driver ixp4xx_spkr_platform_driver = { .owner = THIS_MODULE, }, .probe = ixp4xx_spkr_probe, - .remove = __devexit_p(ixp4xx_spkr_remove), + .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 d99151a8bf1..01f3b5b300f 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb/input.h> @@ -157,7 +156,7 @@ static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed) * though so it's not too big a deal */ if (dev->data.pos >= dev->data.len) { - dev_dbg(&dev->udev->dev, + dev_dbg(&dev->interface->dev, "%s - Error ran out of data. pos: %d, len: %d\n", __func__, dev->data.pos, dev->data.len); return -1; @@ -267,7 +266,9 @@ static void keyspan_check_data(struct usb_keyspan *remote) remote->data.tester = remote->data.tester >> 6; remote->data.bits_left -= 6; } else { - err("%s - Unknown sequence found in system data.\n", __func__); + dev_err(&remote->interface->dev, + "%s - Unknown sequence found in system data.\n", + __func__); remote->stage = 0; return; } @@ -286,7 +287,9 @@ static void keyspan_check_data(struct usb_keyspan *remote) remote->data.tester = remote->data.tester >> 6; remote->data.bits_left -= 6; } else { - err("%s - Unknown sequence found in button data.\n", __func__); + dev_err(&remote->interface->dev, + "%s - Unknown sequence found in button data.\n", + __func__); remote->stage = 0; return; } @@ -302,7 +305,9 @@ static void keyspan_check_data(struct usb_keyspan *remote) remote->data.tester = remote->data.tester >> 6; remote->data.bits_left -= 6; } else { - err("%s - Error in message, invalid toggle.\n", __func__); + dev_err(&remote->interface->dev, + "%s - Error in message, invalid toggle.\n", + __func__); remote->stage = 0; return; } @@ -312,10 +317,11 @@ static void keyspan_check_data(struct usb_keyspan *remote) remote->data.tester = remote->data.tester >> 5; remote->data.bits_left -= 5; } else { - err("Bad message received, no stop bit found.\n"); + dev_err(&remote->interface->dev, + "Bad message received, no stop bit found.\n"); } - dev_dbg(&remote->udev->dev, + dev_dbg(&remote->interface->dev, "%s found valid message: system: %d, button: %d, toggle: %d\n", __func__, message.system, message.button, message.toggle); @@ -397,7 +403,9 @@ static void keyspan_irq_recv(struct urb *urb) resubmit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result: %d", __func__, retval); + dev_err(&dev->interface->dev, + "%s - usb_submit_urb failed with result: %d\n", + __func__, retval); } static int keyspan_open(struct input_dev *dev) diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 783597a9a64..d708478bc5b 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -41,6 +41,14 @@ #define PC1_ON (1 << 7) /* Data ready funtion enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 /* INTERRUPT CONTROL REGISTER 1 BITS */ /* Set these during probe if using irq mode */ #define KXTJ9_IEL (1 << 3) @@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n"); - x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift; - y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift; - z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift; + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); @@ -283,7 +295,7 @@ static void kxtj9_input_close(struct input_dev *dev) kxtj9_disable(tj9); } -static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9, +static void kxtj9_init_input_device(struct kxtj9_data *tj9, struct input_dev *input_dev) { __set_bit(EV_ABS, input_dev->evbit); @@ -296,7 +308,7 @@ static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9, input_dev->dev.parent = &tj9->client->dev; } -static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9) +static int kxtj9_setup_input_device(struct kxtj9_data *tj9) { struct input_dev *input_dev; int err; @@ -421,7 +433,7 @@ static void kxtj9_polled_input_close(struct input_polled_dev *dev) kxtj9_disable(tj9); } -static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9) +static int kxtj9_setup_polled_device(struct kxtj9_data *tj9) { int err; struct input_polled_dev *poll_dev; @@ -454,7 +466,7 @@ static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9) return 0; } -static void __devexit kxtj9_teardown_polled_device(struct kxtj9_data *tj9) +static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9) { input_unregister_polled_device(tj9->poll_dev); input_free_polled_device(tj9->poll_dev); @@ -473,7 +485,7 @@ static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9) #endif -static int __devinit kxtj9_verify(struct kxtj9_data *tj9) +static int kxtj9_verify(struct kxtj9_data *tj9) { int retval; @@ -487,17 +499,18 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9) goto out; } - retval = retval != 0x06 ? -EIO : 0; + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; out: kxtj9_device_power_off(tj9); return retval; } -static int __devinit kxtj9_probe(struct i2c_client *client, +static int kxtj9_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct kxtj9_platform_data *pdata = client->dev.platform_data; + const struct kxtj9_platform_data *pdata = + dev_get_platdata(&client->dev); struct kxtj9_data *tj9; int err; @@ -537,7 +550,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client, i2c_set_clientdata(client, tj9); tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; - tj9->data_ctrl = tj9->pdata.data_odr_init; + tj9->last_poll_interval = tj9->pdata.init_interval; if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ @@ -582,7 +595,7 @@ err_free_mem: return err; } -static int __devexit kxtj9_remove(struct i2c_client *client) +static int kxtj9_remove(struct i2c_client *client) { struct kxtj9_data *tj9 = i2c_get_clientdata(client); @@ -651,21 +664,11 @@ static struct i2c_driver kxtj9_driver = { .pm = &kxtj9_pm_ops, }, .probe = kxtj9_probe, - .remove = __devexit_p(kxtj9_remove), + .remove = kxtj9_remove, .id_table = kxtj9_id, }; -static int __init kxtj9_init(void) -{ - return i2c_add_driver(&kxtj9_driver); -} -module_init(kxtj9_init); - -static void __exit kxtj9_exit(void) -{ - i2c_del_driver(&kxtj9_driver); -} -module_exit(kxtj9_exit); +module_i2c_driver(kxtj9_driver); MODULE_DESCRIPTION("KXTJ9 accelerometer driver"); MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>"); diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c index 0c64d9bb718..def21dc8452 100644 --- a/drivers/input/misc/m68kspkr.c +++ b/drivers/input/misc/m68kspkr.c @@ -48,7 +48,7 @@ static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int return 0; } -static int __devinit m68kspkr_probe(struct platform_device *dev) +static int m68kspkr_probe(struct platform_device *dev) { struct input_dev *input_dev; int err; @@ -80,12 +80,11 @@ static int __devinit m68kspkr_probe(struct platform_device *dev) return 0; } -static int __devexit m68kspkr_remove(struct platform_device *dev) +static int m68kspkr_remove(struct platform_device *dev) { struct input_dev *input_dev = platform_get_drvdata(dev); input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ m68kspkr_event(NULL, EV_SND, SND_BELL, 0); @@ -104,7 +103,7 @@ static struct platform_driver m68kspkr_platform_driver = { .owner = THIS_MODULE, }, .probe = m68kspkr_probe, - .remove = __devexit_p(m68kspkr_remove), + .remove = m68kspkr_remove, .shutdown = m68kspkr_shutdown, }; diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 23cf0827104..3809618e6a5 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -1,5 +1,5 @@ /** - * max8925_onkey.c - MAX8925 ONKEY driver + * MAX8925 ONKEY driver * * Copyright (C) 2009 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/mfd/max8925.h> #include <linux/slab.h> +#include <linux/device.h> #define SW_INPUT (1 << 7) /* 0/1 -- up/down */ #define HARDRESET_EN (1 << 7) @@ -35,7 +36,7 @@ struct max8925_onkey_info { struct input_dev *idev; struct i2c_client *i2c; struct device *dev; - int irq[2]; + unsigned int irq[2]; }; /* @@ -46,17 +47,14 @@ struct max8925_onkey_info { static irqreturn_t max8925_onkey_handler(int irq, void *data) { struct max8925_onkey_info *info = data; - int ret, event; - - ret = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); - if (ret & SW_INPUT) - event = 1; - else - event = 0; - input_report_key(info->idev, KEY_POWER, event); + int state; + + state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); + + input_report_key(info->idev, KEY_POWER, state & SW_INPUT); input_sync(info->idev); - dev_dbg(info->dev, "onkey event:%d\n", event); + dev_dbg(info->dev, "onkey state:%d\n", state); /* Enable hardreset to halt if system isn't shutdown on time */ max8925_set_bits(info->i2c, MAX8925_SYSENSEL, @@ -65,10 +63,11 @@ static irqreturn_t max8925_onkey_handler(int irq, void *data) return IRQ_HANDLED; } -static int __devinit max8925_onkey_probe(struct platform_device *pdev) +static int max8925_onkey_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_onkey_info *info; + struct input_dev *input; int irq[2], error; irq[0] = platform_get_irq(pdev, 0); @@ -76,95 +75,103 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No IRQ resource!\n"); return -EINVAL; } + irq[1] = platform_get_irq(pdev, 1); if (irq[1] < 0) { dev_err(&pdev->dev, "No IRQ resource!\n"); return -EINVAL; } - info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_onkey_info), + GFP_KERNEL); if (!info) return -ENOMEM; + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + info->idev = input; info->i2c = chip->i2c; info->dev = &pdev->dev; - irq[0] += chip->irq_base; - irq[1] += chip->irq_base; + info->irq[0] = irq[0]; + info->irq[1] = irq[1]; - error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler, - IRQF_ONESHOT, "onkey-down", info); + input->name = "max8925_on"; + input->phys = "max8925_on/input0"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + input_set_capability(input, EV_KEY, KEY_POWER); + + error = devm_request_threaded_irq(&pdev->dev, irq[0], NULL, + max8925_onkey_handler, IRQF_ONESHOT, + "onkey-down", info); if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[0], error); - goto out; + return error; } - error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler, - IRQF_ONESHOT, "onkey-up", info); + + error = devm_request_threaded_irq(&pdev->dev, irq[1], NULL, + max8925_onkey_handler, IRQF_ONESHOT, + "onkey-up", info); if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[1], error); - goto out_irq; - } - - info->idev = input_allocate_device(); - if (!info->idev) { - dev_err(chip->dev, "Failed to allocate input dev\n"); - error = -ENOMEM; - goto out_input; + return error; } - info->idev->name = "max8925_on"; - info->idev->phys = "max8925_on/input0"; - info->idev->id.bustype = BUS_I2C; - info->idev->dev.parent = &pdev->dev; - info->irq[0] = irq[0]; - info->irq[1] = irq[1]; - info->idev->evbit[0] = BIT_MASK(EV_KEY); - info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); - - error = input_register_device(info->idev); if (error) { dev_err(chip->dev, "Can't register input device: %d\n", error); - goto out_reg; + return error; } platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); return 0; - -out_reg: - input_free_device(info->idev); -out_input: - free_irq(info->irq[1], info); -out_irq: - free_irq(info->irq[0], info); -out: - kfree(info); - return error; } -static int __devexit max8925_onkey_remove(struct platform_device *pdev) +#ifdef CONFIG_PM_SLEEP +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); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - free_irq(info->irq[0], info); - free_irq(info->irq[1], info); - input_unregister_device(info->idev); - kfree(info); + if (device_may_wakeup(dev)) { + chip->wakeup_flag |= 1 << info->irq[0]; + chip->wakeup_flag |= 1 << info->irq[1]; + } - platform_set_drvdata(pdev, NULL); + return 0; +} + +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); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag &= ~(1 << info->irq[0]); + chip->wakeup_flag &= ~(1 << info->irq[1]); + } return 0; } +#endif + +static 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", .owner = THIS_MODULE, + .pm = &max8925_onkey_pm_ops, }, .probe = max8925_onkey_probe, - .remove = __devexit_p(max8925_onkey_remove), }; module_platform_driver(max8925_onkey_driver); diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c new file mode 100644 index 00000000000..a363ebbd9cc --- /dev/null +++ b/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,416 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * 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> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/pwm.h> +#include <linux/input.h> +#include <linux/mfd/max8997-private.h> +#include <linux/mfd/max8997.h> +#include <linux/regulator/consumer.h> + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool enabled; + unsigned int level; + + struct pwm_device *pwm; + unsigned int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + 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; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + int error; + + mutex_lock(&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->enabled) { + error = regulator_enable(chip->regulator); + if (error) { + dev_err(chip->dev, "Failed to enable regulator\n"); + goto out; + } + 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; + } + } + chip->enabled = true; + } + +out: + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + mutex_lock(&chip->mutex); + + if (chip->enabled) { + chip->enabled = false; + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + } + + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_play_effect_work(struct work_struct *work) +{ + struct max8997_haptic *chip = + container_of(work, struct max8997_haptic, work); + + if (chip->level) + max8997_haptic_enable(chip); + else + max8997_haptic_disable(chip); +} + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + schedule_work(&chip->work); + + return 0; +} + +static void max8997_haptic_close(struct input_dev *dev) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + cancel_work_sync(&chip->work); + max8997_haptic_disable(chip); +} + +static int max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&chip->work, max8997_haptic_play_effect_work); + mutex_init(&chip->mutex); + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + + switch (chip->mode) { + case MAX8997_INTERNAL_MODE: + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + break; + + case MAX8997_EXTERNAL_MODE: + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + break; + + default: + dev_err(&pdev->dev, + "Invalid chip mode specified (%d)\n", chip->mode); + error = -EINVAL; + goto err_free_mem; + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = max8997_haptic_close; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + max8997_haptic_disable(chip); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = max8997_haptic_remove, + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 8428f1e8e83..0df6e8d8bd0 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -20,7 +20,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/input.h> @@ -89,7 +88,7 @@ static irqreturn_t button_irq(int irq, void *_priv) return IRQ_HANDLED; } -static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev) +static int mc13783_pwrbutton_probe(struct platform_device *pdev) { const struct mc13xxx_buttons_platform_data *pdata; struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); @@ -230,7 +229,7 @@ free_input_dev: return err; } -static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev) +static int mc13783_pwrbutton_remove(struct platform_device *pdev) { struct mc13783_pwrb *priv = platform_get_drvdata(pdev); const struct mc13xxx_buttons_platform_data *pdata; @@ -250,14 +249,13 @@ static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev) input_unregister_device(priv->pwr); kfree(priv); - platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver mc13783_pwrbutton_driver = { .probe = mc13783_pwrbutton_probe, - .remove = __devexit_p(mc13783_pwrbutton_remove), + .remove = mc13783_pwrbutton_remove, .driver = { .name = "mc13783-pwrbutton", .owner = THIS_MODULE, diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 4d60080bb5d..59d4dcddf6d 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -123,9 +123,9 @@ static void mma8450_poll(struct input_polled_dev *dev) if (ret < 0) return; - x = ((buf[1] << 4) & 0xff0) | (buf[0] & 0xf); - y = ((buf[3] << 4) & 0xff0) | (buf[2] & 0xf); - z = ((buf[5] << 4) & 0xff0) | (buf[4] & 0xf); + x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf); + 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); @@ -167,8 +167,8 @@ static void mma8450_close(struct input_polled_dev *dev) /* * I2C init/probing/exit functions */ -static int __devinit mma8450_probe(struct i2c_client *c, - const struct i2c_device_id *id) +static int mma8450_probe(struct i2c_client *c, + const struct i2c_device_id *id) { struct input_polled_dev *idev; struct mma8450 *m; @@ -204,6 +204,8 @@ static int __devinit mma8450_probe(struct i2c_client *c, goto err_free_mem; } + i2c_set_clientdata(c, m); + return 0; err_free_mem: @@ -212,7 +214,7 @@ err_free_mem: return err; } -static int __devexit mma8450_remove(struct i2c_client *c) +static int mma8450_remove(struct i2c_client *c) { struct mma8450 *m = i2c_get_clientdata(c); struct input_polled_dev *idev = m->idev; @@ -243,21 +245,11 @@ static struct i2c_driver mma8450_driver = { .of_match_table = mma8450_dt_ids, }, .probe = mma8450_probe, - .remove = __devexit_p(mma8450_remove), + .remove = mma8450_remove, .id_table = mma8450_id, }; -static int __init mma8450_init(void) -{ - return i2c_add_driver(&mma8450_driver); -} -module_init(mma8450_init); - -static void __exit mma8450_exit(void) -{ - i2c_del_driver(&mma8450_driver); -} -module_exit(mma8450_exit); +module_i2c_driver(mma8450_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver"); diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index 208d1a1cc7f..5e5051351c3 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -30,7 +30,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/mutex.h> @@ -257,7 +256,7 @@ static irqreturn_t mpu3050_interrupt_thread(int irq, void *data) * * Called during device probe; configures the sampling method. */ -static int __devinit mpu3050_hw_init(struct mpu3050_sensor *sensor) +static int mpu3050_hw_init(struct mpu3050_sensor *sensor) { struct i2c_client *client = sensor->client; int ret; @@ -306,7 +305,7 @@ static int __devinit mpu3050_hw_init(struct mpu3050_sensor *sensor) * * If present install the relevant sysfs interfaces and input device. */ -static int __devinit mpu3050_probe(struct i2c_client *client, +static int mpu3050_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mpu3050_sensor *sensor; @@ -367,7 +366,7 @@ static int __devinit mpu3050_probe(struct i2c_client *client, error = request_threaded_irq(client->irq, NULL, mpu3050_interrupt_thread, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "mpu3050", sensor); if (error) { dev_err(&client->dev, @@ -383,6 +382,7 @@ static int __devinit mpu3050_probe(struct i2c_client *client, pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY); + i2c_set_clientdata(client, sensor); return 0; @@ -402,7 +402,7 @@ err_free_mem: * * Our sensor is going away, clean up the resources. */ -static int __devexit mpu3050_remove(struct i2c_client *client) +static int mpu3050_remove(struct i2c_client *client) { struct mpu3050_sensor *sensor = i2c_get_clientdata(client); @@ -471,21 +471,11 @@ static struct i2c_driver mpu3050_i2c_driver = { .of_match_table = mpu3050_of_match, }, .probe = mpu3050_probe, - .remove = __devexit_p(mpu3050_remove), + .remove = mpu3050_remove, .id_table = mpu3050_ids, }; -static int __init mpu3050_init(void) -{ - return i2c_add_driver(&mpu3050_i2c_driver); -} -module_init(mpu3050_init); - -static void __exit mpu3050_exit(void) -{ - i2c_del_driver(&mpu3050_i2c_driver); -} -module_exit(mpu3050_exit); +module_i2c_driver(mpu3050_i2c_driver); MODULE_AUTHOR("Wistron Corp."); MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver"); diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c index e09b4fe8191..cd230365166 100644 --- a/drivers/input/misc/pcap_keys.c +++ b/drivers/input/misc/pcap_keys.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/input.h> @@ -48,7 +47,7 @@ static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys) return IRQ_HANDLED; } -static int __devinit pcap_keys_probe(struct platform_device *pdev) +static int pcap_keys_probe(struct platform_device *pdev) { int err = -ENOMEM; struct pcap_keys *pcap_keys; @@ -104,7 +103,7 @@ fail: return err; } -static int __devexit pcap_keys_remove(struct platform_device *pdev) +static int pcap_keys_remove(struct platform_device *pdev) { struct pcap_keys *pcap_keys = platform_get_drvdata(pdev); @@ -119,7 +118,7 @@ static int __devexit pcap_keys_remove(struct platform_device *pdev) static struct platform_driver pcap_keys_device_driver = { .probe = pcap_keys_probe, - .remove = __devexit_p(pcap_keys_remove), + .remove = pcap_keys_remove, .driver = { .name = "pcap-keys", .owner = THIS_MODULE, diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c index 53891de80b0..db92f4f3c99 100644 --- a/drivers/input/misc/pcf50633-input.c +++ b/drivers/input/misc/pcf50633-input.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/input.h> @@ -53,7 +52,7 @@ pcf50633_input_irq(int irq, void *data) input_sync(input->input_dev); } -static int __devinit pcf50633_input_probe(struct platform_device *pdev) +static int pcf50633_input_probe(struct platform_device *pdev) { struct pcf50633_input *input; struct input_dev *input_dev; @@ -93,7 +92,7 @@ static int __devinit pcf50633_input_probe(struct platform_device *pdev) return 0; } -static int __devexit pcf50633_input_remove(struct platform_device *pdev) +static int pcf50633_input_remove(struct platform_device *pdev) { struct pcf50633_input *input = platform_get_drvdata(pdev); @@ -111,7 +110,7 @@ static struct platform_driver pcf50633_input_driver = { .name = "pcf50633-input", }, .probe = pcf50633_input_probe, - .remove = __devexit_p(pcf50633_input_remove), + .remove = pcf50633_input_remove, }; module_platform_driver(pcf50633_input_driver); diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 08be1a35595..97f711a7bd2 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -7,7 +7,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/i2c.h> @@ -82,7 +81,7 @@ static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i, ret; struct input_dev *idev; @@ -113,9 +112,12 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 idev->keycodemax = ARRAY_SIZE(lp->btncode); for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) { - lp->btncode[i] = pcf8574_kp_btncode[i]; - __set_bit(lp->btncode[i] & KEY_MAX, idev->keybit); + if (lp->btncode[i] <= KEY_MAX) { + lp->btncode[i] = pcf8574_kp_btncode[i]; + __set_bit(lp->btncode[i], idev->keybit); + } } + __clear_bit(KEY_RESERVED, idev->keybit); sprintf(lp->name, DRV_NAME); sprintf(lp->phys, "kp_data/input0"); @@ -156,7 +158,7 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 return ret; } -static int __devexit pcf8574_kp_remove(struct i2c_client *client) +static int pcf8574_kp_remove(struct i2c_client *client) { struct kp_data *lp = i2c_get_clientdata(client); @@ -212,21 +214,11 @@ static struct i2c_driver pcf8574_kp_driver = { #endif }, .probe = pcf8574_kp_probe, - .remove = __devexit_p(pcf8574_kp_remove), + .remove = pcf8574_kp_remove, .id_table = pcf8574_kp_id, }; -static int __init pcf8574_kp_init(void) -{ - return i2c_add_driver(&pcf8574_kp_driver); -} -module_init(pcf8574_kp_init); - -static void __exit pcf8574_kp_exit(void) -{ - i2c_del_driver(&pcf8574_kp_driver); -} -module_exit(pcf8574_kp_exit); +module_i2c_driver(pcf8574_kp_driver); MODULE_AUTHOR("Michael Hennerich"); MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574"); diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index b2484aa07f3..674a2cfc3c0 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/i8253.h> -#include <linux/init.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/timex.h> @@ -63,7 +62,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c return 0; } -static int __devinit pcspkr_probe(struct platform_device *dev) +static int pcspkr_probe(struct platform_device *dev) { struct input_dev *pcspkr_dev; int err; @@ -95,12 +94,11 @@ static int __devinit pcspkr_probe(struct platform_device *dev) return 0; } -static int __devexit pcspkr_remove(struct platform_device *dev) +static int pcspkr_remove(struct platform_device *dev) { struct input_dev *pcspkr_dev = platform_get_drvdata(dev); input_unregister_device(pcspkr_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ pcspkr_event(NULL, EV_SND, SND_BELL, 0); @@ -131,7 +129,7 @@ static struct platform_driver pcspkr_platform_driver = { .pm = &pcspkr_pm_ops, }, .probe = pcspkr_probe, - .remove = __devexit_p(pcspkr_remove), + .remove = pcspkr_remove, .shutdown = pcspkr_shutdown, }; module_platform_driver(pcspkr_platform_driver); diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index dfbfb463ea5..6a915ba31bb 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -11,13 +11,12 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/slab.h> -#include <linux/mfd/pm8xxx/core.h> +#include <linux/regmap.h> #define VIB_DRV 0x4A @@ -35,7 +34,7 @@ * struct pm8xxx_vib - structure to hold vibrator data * @vib_input_dev: input device supporting force feedback * @work: work structure to set the vibration parameters - * @dev: device supporting force feedback + * @regmap: regmap for register read/write * @speed: speed of vibration set from userland * @active: state of vibrator * @level: level of vibration to set in the chip @@ -44,7 +43,7 @@ struct pm8xxx_vib { struct input_dev *vib_input_dev; struct work_struct work; - struct device *dev; + struct regmap *regmap; int speed; int level; bool active; @@ -52,42 +51,6 @@ struct pm8xxx_vib { }; /** - * pm8xxx_vib_read_u8 - helper to read a byte from pmic chip - * @vib: pointer to vibrator structure - * @data: placeholder for data to be read - * @reg: register address - */ -static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib, - u8 *data, u16 reg) -{ - int rc; - - rc = pm8xxx_readb(vib->dev->parent, reg, data); - if (rc < 0) - dev_warn(vib->dev, "Error reading pm8xxx reg 0x%x(0x%x)\n", - reg, rc); - return rc; -} - -/** - * pm8xxx_vib_write_u8 - helper to write a byte to pmic chip - * @vib: pointer to vibrator structure - * @data: data to write - * @reg: register address - */ -static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib, - u8 data, u16 reg) -{ - int rc; - - rc = pm8xxx_writeb(vib->dev->parent, reg, data); - if (rc < 0) - dev_warn(vib->dev, "Error writing pm8xxx reg 0x%x(0x%x)\n", - reg, rc); - return rc; -} - -/** * pm8xxx_vib_set - handler to start/stop vibration * @vib: pointer to vibrator structure * @on: state to set @@ -95,14 +58,14 @@ static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib, static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on) { int rc; - u8 val = vib->reg_vib_drv; + unsigned int val = vib->reg_vib_drv; if (on) val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK); else val &= ~VIB_DRV_SEL_MASK; - rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV); + rc = regmap_write(vib->regmap, VIB_DRV, val); if (rc < 0) return rc; @@ -118,9 +81,9 @@ static void pm8xxx_work_handler(struct work_struct *work) { struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work); int rc; - u8 val; + unsigned int val; - rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV); + rc = regmap_read(vib->regmap, VIB_DRV, &val); if (rc < 0) return; @@ -178,40 +141,42 @@ static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data, return 0; } -static int __devinit pm8xxx_vib_probe(struct platform_device *pdev) - +static int pm8xxx_vib_probe(struct platform_device *pdev) { struct pm8xxx_vib *vib; struct input_dev *input_dev; int error; - u8 val; - - vib = kzalloc(sizeof(*vib), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!vib || !input_dev) { - dev_err(&pdev->dev, "couldn't allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; - } + unsigned int val; + + vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL); + if (!vib) + return -ENOMEM; + + vib->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!vib->regmap) + return -ENODEV; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; INIT_WORK(&vib->work, pm8xxx_work_handler); - vib->dev = &pdev->dev; vib->vib_input_dev = input_dev; /* operate in manual mode */ - error = pm8xxx_vib_read_u8(vib, &val, VIB_DRV); + error = regmap_read(vib->regmap, VIB_DRV, &val); if (error < 0) - goto err_free_mem; + return error; + val &= ~VIB_DRV_EN_MANUAL_MASK; - error = pm8xxx_vib_write_u8(vib, val, VIB_DRV); + error = regmap_write(vib->regmap, VIB_DRV, val); if (error < 0) - goto err_free_mem; + return error; vib->reg_vib_drv = val; input_dev->name = "pm8xxx_vib_ffmemless"; input_dev->id.version = 1; - input_dev->dev.parent = &pdev->dev; input_dev->close = pm8xxx_vib_close; input_set_drvdata(input_dev, vib); input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE); @@ -221,37 +186,17 @@ static int __devinit pm8xxx_vib_probe(struct platform_device *pdev) if (error) { dev_err(&pdev->dev, "couldn't register vibrator as FF device\n"); - goto err_free_mem; + return error; } error = input_register_device(input_dev); if (error) { dev_err(&pdev->dev, "couldn't register input device\n"); - goto err_destroy_memless; + return error; } platform_set_drvdata(pdev, vib); return 0; - -err_destroy_memless: - input_ff_destroy(input_dev); -err_free_mem: - input_free_device(input_dev); - kfree(vib); - - return error; -} - -static int __devexit pm8xxx_vib_remove(struct platform_device *pdev) -{ - struct pm8xxx_vib *vib = platform_get_drvdata(pdev); - - input_unregister_device(vib->vib_input_dev); - kfree(vib); - - platform_set_drvdata(pdev, NULL); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -268,13 +213,20 @@ static int pm8xxx_vib_suspend(struct device *dev) static 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" }, + { .compatible = "qcom,pm8921-vib" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table); + static struct platform_driver pm8xxx_vib_driver = { .probe = pm8xxx_vib_probe, - .remove = __devexit_p(pm8xxx_vib_remove), .driver = { .name = "pm8xxx-vib", .owner = THIS_MODULE, .pm = &pm8xxx_vib_pm_ops, + .of_match_table = pm8xxx_vib_id_table, }, }; module_platform_driver(pm8xxx_vib_driver); diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 0f83d0f1d01..c91e3d33aea 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -11,17 +11,15 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/log2.h> - -#include <linux/mfd/pm8xxx/core.h> -#include <linux/input/pmic8xxx-pwrkey.h> +#include <linux/of.h> #define PON_CNTL_1 0x1C #define PON_CNTL_PULL_UP BIT(7) @@ -32,26 +30,25 @@ * @key_press_irq: key press irq number */ struct pmic8xxx_pwrkey { - struct input_dev *pwr; int key_press_irq; }; -static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) +static irqreturn_t pwrkey_press_irq(int irq, void *_pwr) { - struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + struct input_dev *pwr = _pwr; - input_report_key(pwrkey->pwr, KEY_POWER, 1); - input_sync(pwrkey->pwr); + input_report_key(pwr, KEY_POWER, 1); + input_sync(pwr); return IRQ_HANDLED; } -static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) +static irqreturn_t pwrkey_release_irq(int irq, void *_pwr) { - struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + struct input_dev *pwr = _pwr; - input_report_key(pwrkey->pwr, KEY_POWER, 0); - input_sync(pwrkey->pwr); + input_report_key(pwr, KEY_POWER, 0); + input_sync(pwr); return IRQ_HANDLED; } @@ -81,136 +78,126 @@ static int pmic8xxx_pwrkey_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); -static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) +static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) { struct input_dev *pwr; int key_release_irq = platform_get_irq(pdev, 0); int key_press_irq = platform_get_irq(pdev, 1); int err; unsigned int delay; - u8 pon_cntl; + unsigned int pon_cntl; + struct regmap *regmap; struct pmic8xxx_pwrkey *pwrkey; - const struct pm8xxx_pwrkey_platform_data *pdata = - dev_get_platdata(&pdev->dev); + u32 kpd_delay; + bool pull_up; - if (!pdata) { - dev_err(&pdev->dev, "power key platform data not supplied\n"); - return -EINVAL; - } + if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay)) + kpd_delay = 15625; - if (pdata->kpd_trigger_delay_us > 62500) { + if (kpd_delay > 62500 || kpd_delay == 0) { dev_err(&pdev->dev, "invalid power key trigger delay\n"); return -EINVAL; } - pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); + pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up"); + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "failed to locate regmap for the device\n"); + return -ENODEV; + } + + pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); if (!pwrkey) return -ENOMEM; - pwr = input_allocate_device(); + pwrkey->key_press_irq = key_press_irq; + + pwr = devm_input_allocate_device(&pdev->dev); if (!pwr) { dev_dbg(&pdev->dev, "Can't allocate power button\n"); - err = -ENOMEM; - goto free_pwrkey; + return -ENOMEM; } input_set_capability(pwr, EV_KEY, KEY_POWER); pwr->name = "pmic8xxx_pwrkey"; pwr->phys = "pmic8xxx_pwrkey/input0"; - pwr->dev.parent = &pdev->dev; - delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = (kpd_delay << 10) / USEC_PER_SEC; delay = 1 + ilog2(delay); - err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); + err = regmap_read(regmap, PON_CNTL_1, &pon_cntl); if (err < 0) { dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); - goto free_input_dev; + return err; } pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); - if (pdata->pull_up) + if (pull_up) pon_cntl |= PON_CNTL_PULL_UP; else pon_cntl &= ~PON_CNTL_PULL_UP; - err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); + err = regmap_write(regmap, PON_CNTL_1, pon_cntl); if (err < 0) { dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); - goto free_input_dev; + return err; } - err = input_register_device(pwr); + err = devm_request_irq(&pdev->dev, key_press_irq, pwrkey_press_irq, + IRQF_TRIGGER_RISING, + "pmic8xxx_pwrkey_press", pwr); if (err) { - dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); - goto free_input_dev; + dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_press_irq, err); + return err; } - pwrkey->key_press_irq = key_press_irq; - pwrkey->pwr = pwr; - - platform_set_drvdata(pdev, pwrkey); - - err = request_irq(key_press_irq, pwrkey_press_irq, - IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); - if (err < 0) { - dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", - key_press_irq, err); - goto unreg_input_dev; + err = devm_request_irq(&pdev->dev, key_release_irq, pwrkey_release_irq, + IRQF_TRIGGER_RISING, + "pmic8xxx_pwrkey_release", pwr); + if (err) { + dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_release_irq, err); + return err; } - err = request_irq(key_release_irq, pwrkey_release_irq, - IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); - if (err < 0) { - dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", - key_release_irq, err); - - goto free_press_irq; + err = input_register_device(pwr); + if (err) { + dev_err(&pdev->dev, "Can't register power key: %d\n", err); + return err; } - device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, pwrkey); + device_init_wakeup(&pdev->dev, 1); return 0; - -free_press_irq: - free_irq(key_press_irq, NULL); -unreg_input_dev: - platform_set_drvdata(pdev, NULL); - input_unregister_device(pwr); - pwr = NULL; -free_input_dev: - input_free_device(pwr); -free_pwrkey: - kfree(pwrkey); - return err; } -static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) +static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) { - struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); - int key_release_irq = platform_get_irq(pdev, 0); - int key_press_irq = platform_get_irq(pdev, 1); - device_init_wakeup(&pdev->dev, 0); - free_irq(key_press_irq, pwrkey); - free_irq(key_release_irq, pwrkey); - input_unregister_device(pwrkey->pwr); - platform_set_drvdata(pdev, NULL); - kfree(pwrkey); - return 0; } +static const struct of_device_id pm8xxx_pwr_key_id_table[] = { + { .compatible = "qcom,pm8058-pwrkey" }, + { .compatible = "qcom,pm8921-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); + static struct platform_driver pmic8xxx_pwrkey_driver = { .probe = pmic8xxx_pwrkey_probe, - .remove = __devexit_p(pmic8xxx_pwrkey_remove), + .remove = pmic8xxx_pwrkey_remove, .driver = { - .name = PM8XXX_PWRKEY_DEV_NAME, + .name = "pm8xxx-pwrkey", .owner = THIS_MODULE, .pm = &pm8xxx_pwr_key_pm_ops, + .of_match_table = pm8xxx_pwr_key_id_table, }, }; module_platform_driver(pmic8xxx_pwrkey_driver); diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index 538f7049ec6..63b539d3dab 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/spinlock.h> #include <linux/usb/input.h> @@ -65,6 +64,7 @@ struct powermate_device { struct urb *irq, *config; struct usb_ctrlrequest *configcr; struct usb_device *udev; + struct usb_interface *intf; struct input_dev *input; spinlock_t lock; int static_brightness; @@ -85,6 +85,7 @@ static void powermate_config_complete(struct urb *urb); static void powermate_irq(struct urb *urb) { struct powermate_device *pm = urb->context; + struct device *dev = &pm->intf->dev; int retval; switch (urb->status) { @@ -95,10 +96,12 @@ static void powermate_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); + dev_dbg(dev, "%s - urb shutting down with status: %d\n", + __func__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); + dev_dbg(dev, "%s - nonzero urb status received: %d\n", + __func__, urb->status); goto exit; } @@ -110,8 +113,8 @@ static void powermate_irq(struct urb *urb) exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(dev, "%s - usb_submit_urb failed with result: %d\n", + __func__, retval); } /* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ @@ -330,6 +333,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i goto fail3; pm->udev = udev; + pm->intf = intf; pm->input = input_dev; usb_make_path(udev, pm->phys, sizeof(pm->phys)); diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index fc84c8a5114..8ef288e7c97 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -16,6 +16,7 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -65,9 +66,9 @@ static int pwm_beeper_event(struct input_dev *input, return 0; } -static int __devinit pwm_beeper_probe(struct platform_device *pdev) +static int pwm_beeper_probe(struct platform_device *pdev) { - unsigned long pwm_id = (unsigned long)pdev->dev.platform_data; + unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); struct pwm_beeper *beeper; int error; @@ -75,7 +76,11 @@ static int __devinit pwm_beeper_probe(struct platform_device *pdev) if (!beeper) return -ENOMEM; - beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + beeper->pwm = pwm_get(&pdev->dev, NULL); + if (IS_ERR(beeper->pwm)) { + dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n"); + beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + } if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); @@ -125,11 +130,10 @@ err_free: return error; } -static int __devexit pwm_beeper_remove(struct platform_device *pdev) +static int pwm_beeper_remove(struct platform_device *pdev) { struct pwm_beeper *beeper = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(beeper->input); pwm_disable(beeper->pwm); @@ -140,7 +144,7 @@ static int __devexit pwm_beeper_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); @@ -171,13 +175,21 @@ static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, #define PWM_BEEPER_PM_OPS NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id pwm_beeper_match[] = { + { .compatible = "pwm-beeper", }, + { }, +}; +#endif + static struct platform_driver pwm_beeper_driver = { .probe = pwm_beeper_probe, - .remove = __devexit_p(pwm_beeper_remove), + .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", .owner = THIS_MODULE, .pm = PWM_BEEPER_PM_OPS, + .of_match_table = of_match_ptr(pwm_beeper_match), }, }; module_platform_driver(pwm_beeper_driver); diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c index aeb02bcf723..83fff38b86b 100644 --- a/drivers/input/misc/rb532_button.c +++ b/drivers/input/misc/rb532_button.c @@ -51,7 +51,7 @@ static void rb532_button_poll(struct input_polled_dev *poll_dev) input_sync(poll_dev->input); } -static int __devinit rb532_button_probe(struct platform_device *pdev) +static int rb532_button_probe(struct platform_device *pdev) { struct input_polled_dev *poll_dev; int error; @@ -81,20 +81,19 @@ static int __devinit rb532_button_probe(struct platform_device *pdev) return 0; } -static int __devexit rb532_button_remove(struct platform_device *pdev) +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); - dev_set_drvdata(&pdev->dev, NULL); return 0; } static struct platform_driver rb532_button_driver = { .probe = rb532_button_probe, - .remove = __devexit_p(rb532_button_remove), + .remove = rb532_button_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c new file mode 100644 index 00000000000..4bff1aa9b0d --- /dev/null +++ b/drivers/input/misc/retu-pwrbutton.c @@ -0,0 +1,98 @@ +/* + * Retu power button driver. + * + * Copyright (C) 2004-2010 Nokia Corporation + * + * Original code written by Ari Saastamoinen, Juha Yrjölä and Felipe Balbi. + * Rewritten by Aaro Koskinen. + * + * 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/irq.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/retu.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#define RETU_STATUS_PWRONX (1 << 5) + +static irqreturn_t retu_pwrbutton_irq(int irq, void *_pwr) +{ + struct input_dev *idev = _pwr; + struct retu_dev *rdev = input_get_drvdata(idev); + bool state; + + state = !(retu_read(rdev, RETU_REG_STATUS) & RETU_STATUS_PWRONX); + input_report_key(idev, KEY_POWER, state); + input_sync(idev); + + return IRQ_HANDLED; +} + +static int retu_pwrbutton_probe(struct platform_device *pdev) +{ + struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); + struct input_dev *idev; + int irq; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + idev = devm_input_allocate_device(&pdev->dev); + if (!idev) + return -ENOMEM; + + idev->name = "retu-pwrbutton"; + idev->dev.parent = &pdev->dev; + + input_set_capability(idev, EV_KEY, KEY_POWER); + input_set_drvdata(idev, rdev); + + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, retu_pwrbutton_irq, 0, + "retu-pwrbutton", idev); + if (error) + return error; + + error = input_register_device(idev); + if (error) + return error; + + return 0; +} + +static int retu_pwrbutton_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver retu_pwrbutton_driver = { + .probe = retu_pwrbutton_probe, + .remove = retu_pwrbutton_remove, + .driver = { + .name = "retu-pwrbutton", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(retu_pwrbutton_driver); + +MODULE_ALIAS("platform:retu-pwrbutton"); +MODULE_DESCRIPTION("Retu Power Button"); +MODULE_AUTHOR("Ari Saastamoinen"); +MODULE_AUTHOR("Felipe Balbi"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index f07f784198b..93558a1c7f7 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/device.h> @@ -24,12 +23,15 @@ #include <linux/gpio.h> #include <linux/rotary_encoder.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> #define DRV_NAME "rotary-encoder" struct rotary_encoder { struct input_dev *input; - struct rotary_encoder_platform_data *pdata; + const struct rotary_encoder_platform_data *pdata; unsigned int axis; unsigned int pos; @@ -43,7 +45,7 @@ struct rotary_encoder { char last_stable; }; -static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata) +static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) { int a = !!gpio_get_value(pdata->gpio_a); int b = !!gpio_get_value(pdata->gpio_b); @@ -56,7 +58,7 @@ static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata) static void rotary_encoder_report_event(struct rotary_encoder *encoder) { - struct rotary_encoder_platform_data *pdata = encoder->pdata; + const struct rotary_encoder_platform_data *pdata = encoder->pdata; if (pdata->relative_axis) { input_report_rel(encoder->input, @@ -140,36 +142,88 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit rotary_encoder_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static const struct of_device_id rotary_encoder_of_match[] = { + { .compatible = "rotary-encoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); + +static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev) { - struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + const struct of_device_id *of_id = + of_match_device(rotary_encoder_of_match, dev); + struct device_node *np = dev->of_node; + struct rotary_encoder_platform_data *pdata; + enum of_gpio_flags flags; + + if (!of_id || !np) + return NULL; + + pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); + of_property_read_u32(np, "linux,axis", &pdata->axis); + + pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); + pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; + + pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); + pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; + + pdata->relative_axis = !!of_get_property(np, + "rotary-encoder,relative-axis", NULL); + pdata->rollover = !!of_get_property(np, + "rotary-encoder,rollover", NULL); + pdata->half_period = !!of_get_property(np, + "rotary-encoder,half-period", NULL); + + return pdata; +} +#else +static inline struct rotary_encoder_platform_data * +rotary_encoder_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + +static int rotary_encoder_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); struct rotary_encoder *encoder; struct input_dev *input; irq_handler_t handler; int err; if (!pdata) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENOENT; + pdata = rotary_encoder_parse_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + if (!pdata) { + dev_err(dev, "missing platform data\n"); + return -EINVAL; + } } encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); input = input_allocate_device(); if (!encoder || !input) { - dev_err(&pdev->dev, "failed to allocate memory for device\n"); err = -ENOMEM; goto exit_free_mem; } encoder->input = input; encoder->pdata = pdata; - encoder->irq_a = gpio_to_irq(pdata->gpio_a); - encoder->irq_b = gpio_to_irq(pdata->gpio_b); - /* create and register the input driver */ input->name = pdev->name; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; + input->dev.parent = dev; if (pdata->relative_axis) { input->evbit[0] = BIT_MASK(EV_REL); @@ -180,40 +234,21 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) pdata->axis, 0, pdata->steps, 0, 1); } - err = input_register_device(input); - if (err) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto exit_free_mem; - } - /* request the GPIOs */ - err = gpio_request(pdata->gpio_a, DRV_NAME); + err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); if (err) { - dev_err(&pdev->dev, "unable to request GPIO %d\n", - pdata->gpio_a); - goto exit_unregister_input; - } - - err = gpio_direction_input(pdata->gpio_a); - if (err) { - dev_err(&pdev->dev, "unable to set GPIO %d for input\n", - pdata->gpio_a); - goto exit_unregister_input; + dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); + goto exit_free_mem; } - err = gpio_request(pdata->gpio_b, DRV_NAME); + err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); if (err) { - dev_err(&pdev->dev, "unable to request GPIO %d\n", - pdata->gpio_b); + dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); goto exit_free_gpio_a; } - err = gpio_direction_input(pdata->gpio_b); - if (err) { - dev_err(&pdev->dev, "unable to set GPIO %d for input\n", - pdata->gpio_b); - goto exit_free_gpio_a; - } + encoder->irq_a = gpio_to_irq(pdata->gpio_a); + encoder->irq_b = gpio_to_irq(pdata->gpio_b); /* request the IRQs */ if (pdata->half_period) { @@ -227,8 +262,7 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRV_NAME, encoder); if (err) { - dev_err(&pdev->dev, "unable to request IRQ %d\n", - encoder->irq_a); + dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); goto exit_free_gpio_b; } @@ -236,52 +270,63 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRV_NAME, encoder); if (err) { - dev_err(&pdev->dev, "unable to request IRQ %d\n", - encoder->irq_b); + dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); goto exit_free_irq_a; } + err = input_register_device(input); + if (err) { + dev_err(dev, "failed to register input device\n"); + goto exit_free_irq_b; + } + platform_set_drvdata(pdev, encoder); return 0; +exit_free_irq_b: + free_irq(encoder->irq_b, encoder); exit_free_irq_a: free_irq(encoder->irq_a, encoder); exit_free_gpio_b: gpio_free(pdata->gpio_b); exit_free_gpio_a: gpio_free(pdata->gpio_a); -exit_unregister_input: - input_unregister_device(input); - input = NULL; /* so we don't try to free it */ exit_free_mem: input_free_device(input); kfree(encoder); + if (!dev_get_platdata(&pdev->dev)) + kfree(pdata); + return err; } -static int __devexit rotary_encoder_remove(struct platform_device *pdev) +static int rotary_encoder_remove(struct platform_device *pdev) { struct rotary_encoder *encoder = platform_get_drvdata(pdev); - struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + const struct rotary_encoder_platform_data *pdata = encoder->pdata; free_irq(encoder->irq_a, encoder); free_irq(encoder->irq_b, encoder); gpio_free(pdata->gpio_a); gpio_free(pdata->gpio_b); + input_unregister_device(encoder->input); - platform_set_drvdata(pdev, NULL); kfree(encoder); + if (!dev_get_platdata(&pdev->dev)) + kfree(pdata); + return 0; } static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, - .remove = __devexit_p(rotary_encoder_remove), + .remove = rotary_encoder_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rotary_encoder_of_match), } }; module_platform_driver(rotary_encoder_driver); diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c index 5d9fd557119..f10474937a6 100644 --- a/drivers/input/misc/sgi_btns.c +++ b/drivers/input/misc/sgi_btns.c @@ -17,7 +17,6 @@ * 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/init.h> #include <linux/input-polldev.h> #include <linux/ioport.h> #include <linux/module.h> @@ -91,7 +90,7 @@ static void handle_buttons(struct input_polled_dev *dev) } } -static int __devinit sgi_buttons_probe(struct platform_device *pdev) +static int sgi_buttons_probe(struct platform_device *pdev) { struct buttons_dev *bdev; struct input_polled_dev *poll_dev; @@ -128,7 +127,7 @@ static int __devinit sgi_buttons_probe(struct platform_device *pdev) __clear_bit(KEY_RESERVED, input->keybit); bdev->poll_dev = poll_dev; - dev_set_drvdata(&pdev->dev, bdev); + platform_set_drvdata(pdev, bdev); error = input_register_polled_device(poll_dev); if (error) @@ -139,26 +138,23 @@ static int __devinit sgi_buttons_probe(struct platform_device *pdev) err_free_mem: input_free_polled_device(poll_dev); kfree(bdev); - dev_set_drvdata(&pdev->dev, NULL); return error; } -static int __devexit sgi_buttons_remove(struct platform_device *pdev) +static int sgi_buttons_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct buttons_dev *bdev = dev_get_drvdata(dev); + struct buttons_dev *bdev = platform_get_drvdata(pdev); input_unregister_polled_device(bdev->poll_dev); input_free_polled_device(bdev->poll_dev); kfree(bdev); - dev_set_drvdata(dev, NULL); return 0; } static struct platform_driver sgi_buttons_driver = { .probe = sgi_buttons_probe, - .remove = __devexit_p(sgi_buttons_remove), + .remove = sgi_buttons_remove, .driver = { .name = "sgibtns", .owner = THIS_MODULE, diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c new file mode 100644 index 00000000000..fed5102e180 --- /dev/null +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -0,0 +1,219 @@ +/* + * 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 sirfsoc_pwrc_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int 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; +} +#endif + +static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume); + +static struct platform_driver sirfsoc_pwrc_driver = { + .probe = sirfsoc_pwrc_probe, + .remove = sirfsoc_pwrc_remove, + .driver = { + .name = "sirfsoc-pwrc", + .owner = THIS_MODULE, + .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 new file mode 100644 index 00000000000..5a6334be30b --- /dev/null +++ b/drivers/input/misc/soc_button_array.c @@ -0,0 +1,218 @@ +/* + * 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/kernel.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio_keys.h> +#include <linux/platform_device.h> +#include <linux/pnp.h> + +/* + * Definition of buttons on the tablet. The ACPI index of each button + * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC + * Platforms" + */ +#define MAX_NBUTTONS 5 + +struct soc_button_info { + const char *name; + int acpi_index; + unsigned int event_type; + unsigned int event_code; + bool autorepeat; + bool wakeup; +}; + +/* + * Some of the buttons like volume up/down are auto repeat, while others + * are not. To support both, we register two platform devices, and put + * buttons into them based on whether the key should be auto repeat. + */ +#define BUTTON_TYPES 2 + +struct soc_button_data { + struct platform_device *children[BUTTON_TYPES]; +}; + +/* + * Get the Nth GPIO number from the ACPI object. + */ +static int soc_button_lookup_gpio(struct device *dev, int acpi_index) +{ + struct gpio_desc *desc; + int gpio; + + desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + gpio = desc_to_gpio(desc); + + gpiod_put(desc); + + return gpio; +} + +static struct platform_device * +soc_button_device_create(struct pnp_dev *pdev, + const struct soc_button_info *button_info, + bool autorepeat) +{ + const struct soc_button_info *info; + struct platform_device *pd; + struct gpio_keys_button *gpio_keys; + struct gpio_keys_platform_data *gpio_keys_pdata; + int n_buttons = 0; + int gpio; + int error; + + gpio_keys_pdata = devm_kzalloc(&pdev->dev, + sizeof(*gpio_keys_pdata) + + sizeof(*gpio_keys) * MAX_NBUTTONS, + GFP_KERNEL); + gpio_keys = (void *)(gpio_keys_pdata + 1); + + for (info = button_info; info->name; info++) { + if (info->autorepeat != autorepeat) + continue; + + gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); + if (gpio < 0) + continue; + + 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].desc = info->name; + gpio_keys[n_buttons].wakeup = info->wakeup; + n_buttons++; + } + + if (n_buttons == 0) { + error = -ENODEV; + goto err_free_mem; + } + + gpio_keys_pdata->buttons = gpio_keys; + gpio_keys_pdata->nbuttons = n_buttons; + gpio_keys_pdata->rep = autorepeat; + + pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); + if (!pd) { + error = -ENOMEM; + 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); +} + +static void soc_button_remove(struct pnp_dev *pdev) +{ + struct soc_button_data *priv = pnp_get_drvdata(pdev); + int i; + + for (i = 0; i < BUTTON_TYPES; i++) + if (priv->children[i]) + platform_device_unregister(priv->children[i]); +} + +static int soc_button_pnp_probe(struct pnp_dev *pdev, + const struct pnp_device_id *id) +{ + const struct soc_button_info *button_info = (void *)id->driver_data; + struct soc_button_data *priv; + struct platform_device *pd; + int i; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pnp_set_drvdata(pdev, priv); + + for (i = 0; i < BUTTON_TYPES; i++) { + pd = soc_button_device_create(pdev, button_info, i == 0); + if (IS_ERR(pd)) { + error = PTR_ERR(pd); + if (error != -ENODEV) { + soc_button_remove(pdev); + return error; + } + continue; + } + + priv->children[i] = pd; + } + + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + + return 0; +} + +static struct soc_button_info soc_button_PNP0C40[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true }, + { "home", 1, EV_KEY, KEY_HOME, 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 pnp_device_id soc_button_pnp_match[] = { + { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); + +static struct pnp_driver soc_button_pnp_driver = { + .name = KBUILD_MODNAME, + .id_table = soc_button_pnp_match, + .probe = soc_button_pnp_probe, + .remove = soc_button_remove, +}; + +static int __init soc_button_init(void) +{ + return pnp_register_driver(&soc_button_pnp_driver); +} + +static void __exit soc_button_exit(void) +{ + pnp_unregister_driver(&soc_button_pnp_driver); +} + +module_init(soc_button_init); +module_exit(soc_button_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 0122f535157..65fd3150919 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -139,7 +139,7 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned return 0; } -static int __devinit sparcspkr_probe(struct device *dev) +static int sparcspkr_probe(struct device *dev) { struct sparcspkr_state *state = dev_get_drvdata(dev); struct input_dev *input_dev; @@ -175,14 +175,14 @@ static int __devinit sparcspkr_probe(struct device *dev) static void sparcspkr_shutdown(struct platform_device *dev) { - struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); + struct sparcspkr_state *state = platform_get_drvdata(dev); struct input_dev *input_dev = state->input_dev; /* turn off the speaker */ state->event(input_dev, EV_SND, SND_BELL, 0); } -static int __devinit bbc_beep_probe(struct platform_device *op) +static int bbc_beep_probe(struct platform_device *op) { struct sparcspkr_state *state; struct bbc_beep_info *info; @@ -211,7 +211,7 @@ static int __devinit bbc_beep_probe(struct platform_device *op) if (!info->regs) goto out_free; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -220,7 +220,6 @@ static int __devinit bbc_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[0], info->regs, 6); out_free: @@ -229,9 +228,9 @@ out_err: return err; } -static int __devexit bbc_remove(struct platform_device *op) +static int bbc_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct input_dev *input_dev = state->input_dev; struct bbc_beep_info *info = &state->u.bbc; @@ -242,7 +241,6 @@ static int __devexit bbc_remove(struct platform_device *op) of_iounmap(&op->resource[0], info->regs, 6); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; @@ -263,11 +261,11 @@ static struct platform_driver bbc_beep_driver = { .of_match_table = bbc_beep_match, }, .probe = bbc_beep_probe, - .remove = __devexit_p(bbc_remove), + .remove = bbc_remove, .shutdown = sparcspkr_shutdown, }; -static int __devinit grover_beep_probe(struct platform_device *op) +static int grover_beep_probe(struct platform_device *op) { struct sparcspkr_state *state; struct grover_beep_info *info; @@ -290,7 +288,7 @@ static int __devinit grover_beep_probe(struct platform_device *op) if (!info->enable_reg) goto out_unmap_freq_regs; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -299,7 +297,6 @@ static int __devinit grover_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[3], info->enable_reg, 1); out_unmap_freq_regs: @@ -310,9 +307,9 @@ out_err: return err; } -static int __devexit grover_remove(struct platform_device *op) +static int grover_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct grover_beep_info *info = &state->u.grover; struct input_dev *input_dev = state->input_dev; @@ -324,7 +321,6 @@ static int __devexit grover_remove(struct platform_device *op) of_iounmap(&op->resource[3], info->enable_reg, 1); of_iounmap(&op->resource[2], info->freq_regs, 2); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; @@ -345,7 +341,7 @@ static struct platform_driver grover_beep_driver = { .of_match_table = grover_beep_match, }, .probe = grover_beep_probe, - .remove = __devexit_p(grover_remove), + .remove = grover_remove, .shutdown = sparcspkr_shutdown, }; diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index 38e4b507b94..fb3b63b2f85 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -39,9 +39,9 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr) int err; u8 value; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value, - STS_HW_CONDITIONS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &value, STS_HW_CONDITIONS); if (!err) { + pm_wakeup_event(pwr->dev.parent, 0); input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ); input_sync(pwr); } else { @@ -52,15 +52,15 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr) return IRQ_HANDLED; } -static int __init twl4030_pwrbutton_probe(struct platform_device *pdev) +static int twl4030_pwrbutton_probe(struct platform_device *pdev) { struct input_dev *pwr; int irq = platform_get_irq(pdev, 0); int err; - pwr = input_allocate_device(); + pwr = devm_input_allocate_device(&pdev->dev); if (!pwr) { - dev_dbg(&pdev->dev, "Can't allocate power button\n"); + dev_err(&pdev->dev, "Can't allocate power button\n"); return -ENOMEM; } @@ -70,62 +70,42 @@ static int __init twl4030_pwrbutton_probe(struct platform_device *pdev) pwr->phys = "twl4030_pwrbutton/input0"; pwr->dev.parent = &pdev->dev; - err = request_threaded_irq(irq, NULL, powerbutton_irq, + err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "twl4030_pwrbutton", pwr); if (err < 0) { - dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); - goto free_input_dev; + dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); + return err; } err = input_register_device(pwr); if (err) { - dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); - goto free_irq; + dev_err(&pdev->dev, "Can't register power button: %d\n", err); + return err; } platform_set_drvdata(pdev, pwr); return 0; - -free_irq: - free_irq(irq, pwr); -free_input_dev: - input_free_device(pwr); - return err; } -static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev) -{ - struct input_dev *pwr = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - free_irq(irq, pwr); - input_unregister_device(pwr); - - return 0; -} +#ifdef CONFIG_OF +static const struct of_device_id twl4030_pwrbutton_dt_match_table[] = { + { .compatible = "ti,twl4030-pwrbutton" }, + {}, +}; +MODULE_DEVICE_TABLE(of, twl4030_pwrbutton_dt_match_table); +#endif static struct platform_driver twl4030_pwrbutton_driver = { - .remove = __exit_p(twl4030_pwrbutton_remove), + .probe = twl4030_pwrbutton_probe, .driver = { .name = "twl4030_pwrbutton", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl4030_pwrbutton_dt_match_table), }, }; - -static int __init twl4030_pwrbutton_init(void) -{ - return platform_driver_probe(&twl4030_pwrbutton_driver, - twl4030_pwrbutton_probe); -} -module_init(twl4030_pwrbutton_init); - -static void __exit twl4030_pwrbutton_exit(void) -{ - platform_driver_unregister(&twl4030_pwrbutton_driver); -} -module_exit(twl4030_pwrbutton_exit); +module_platform_driver(twl4030_pwrbutton_driver); MODULE_ALIAS("platform:twl4030_pwrbutton"); MODULE_DESCRIPTION("Triton2 Power Button"); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index f3bc4189a7b..960ef2a7091 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/jiffies.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/workqueue.h> #include <linux/i2c/twl.h> #include <linux/mfd/twl4030-audio.h> @@ -42,7 +43,6 @@ struct vibra_info { struct device *dev; struct input_dev *input_dev; - struct workqueue_struct *workqueue; struct work_struct play_work; bool enabled; @@ -142,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data, if (!info->speed) info->speed = effect->u.rumble.weak_magnitude >> 9; info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; - queue_work(info->workqueue, &info->play_work); - return 0; -} - -static int twl4030_vibra_open(struct input_dev *input) -{ - struct vibra_info *info = input_get_drvdata(input); - - info->workqueue = create_singlethread_workqueue("vibra"); - if (info->workqueue == NULL) { - dev_err(&input->dev, "couldn't create workqueue\n"); - return -ENOMEM; - } + schedule_work(&info->play_work); return 0; } @@ -163,16 +151,13 @@ static void twl4030_vibra_close(struct input_dev *input) struct vibra_info *info = input_get_drvdata(input); cancel_work_sync(&info->play_work); - INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */ - destroy_workqueue(info->workqueue); - info->workqueue = NULL; if (info->enabled) vibra_disable(info); } /*** Module ***/ -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int twl4030_vibra_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -194,30 +179,44 @@ static int twl4030_vibra_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, twl4030_vibra_suspend, twl4030_vibra_resume); -static int __devinit twl4030_vibra_probe(struct platform_device *pdev) +static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, + struct device_node *node) +{ + if (pdata && pdata->coexist) + return true; + + if (of_find_node_by_name(node, "codec")) { + of_node_put(node); + return true; + } + + return false; +} + +static int twl4030_vibra_probe(struct platform_device *pdev) { - struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + 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) { + if (!pdata && !twl4030_core_node) { dev_dbg(&pdev->dev, "platform_data not available\n"); return -EINVAL; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->dev = &pdev->dev; - info->coexist = pdata->coexist; + info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); INIT_WORK(&info->play_work, vibra_play_work); - info->input_dev = input_allocate_device(); + info->input_dev = devm_input_allocate_device(&pdev->dev); if (info->input_dev == NULL) { dev_err(&pdev->dev, "couldn't allocate input device\n"); - ret = -ENOMEM; - goto err_kzalloc; + return -ENOMEM; } input_set_drvdata(info->input_dev, info); @@ -225,14 +224,13 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev) info->input_dev->name = "twl4030:vibrator"; info->input_dev->id.version = 1; info->input_dev->dev.parent = pdev->dev.parent; - info->input_dev->open = twl4030_vibra_open; info->input_dev->close = twl4030_vibra_close; __set_bit(FF_RUMBLE, info->input_dev->ffbit); ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); - goto err_ialloc; + return ret; } ret = input_register_device(info->input_dev); @@ -248,28 +246,11 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev) err_iff: input_ff_destroy(info->input_dev); -err_ialloc: - input_free_device(info->input_dev); -err_kzalloc: - kfree(info); return ret; } -static int __devexit twl4030_vibra_remove(struct platform_device *pdev) -{ - struct vibra_info *info = platform_get_drvdata(pdev); - - /* this also free ff-memless and calls close if needed */ - input_unregister_device(info->input_dev); - kfree(info); - platform_set_drvdata(pdev, NULL); - - return 0; -} - static struct platform_driver twl4030_vibra_driver = { .probe = twl4030_vibra_probe, - .remove = __devexit_p(twl4030_vibra_remove), .driver = { .name = "twl4030-vibra", .owner = THIS_MODULE, diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 45874fed523..6d26eecc278 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -27,8 +27,9 @@ */ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/workqueue.h> -#include <linux/i2c/twl.h> +#include <linux/input.h> #include <linux/mfd/twl6040.h> #include <linux/slab.h> #include <linux/delay.h> @@ -250,160 +251,141 @@ static int twl6040_vibra_suspend(struct device *dev) return 0; } - #endif static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL); -static int __devinit twl6040_vibra_probe(struct platform_device *pdev) +static int twl6040_vibra_probe(struct platform_device *pdev) { - struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + struct device *twl6040_core_dev = pdev->dev.parent; + struct device_node *twl6040_core_node; struct vibra_info *info; - int ret; - - if (!pdata) { - dev_err(&pdev->dev, "platform_data not available\n"); + int vddvibl_uV = 0; + int vddvibr_uV = 0; + int error; + + twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node, + "vibra"); + if (!twl6040_core_node) { + dev_err(&pdev->dev, "parent of node is missing?\n"); return -EINVAL; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + 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; } info->dev = &pdev->dev; + info->twl6040 = dev_get_drvdata(pdev->dev.parent); - info->vibldrv_res = pdata->vibldrv_res; - info->vibrdrv_res = pdata->vibrdrv_res; - info->viblmotor_res = pdata->viblmotor_res; - info->vibrmotor_res = pdata->vibrmotor_res; + + of_property_read_u32(twl6040_core_node, "ti,vibldrv-res", + &info->vibldrv_res); + of_property_read_u32(twl6040_core_node, "ti,vibrdrv-res", + &info->vibrdrv_res); + of_property_read_u32(twl6040_core_node, "ti,viblmotor-res", + &info->viblmotor_res); + of_property_read_u32(twl6040_core_node, "ti,vibrmotor-res", + &info->vibrmotor_res); + 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"); - ret = -EINVAL; - goto err_kzalloc; + return -EINVAL; } info->irq = platform_get_irq(pdev, 0); if (info->irq < 0) { dev_err(info->dev, "invalid irq\n"); - ret = -EINVAL; - goto err_kzalloc; + return -EINVAL; } mutex_init(&info->mutex); - info->input_dev = input_allocate_device(); - if (info->input_dev == NULL) { - dev_err(info->dev, "couldn't allocate input device\n"); - ret = -ENOMEM; - goto err_kzalloc; - } - - input_set_drvdata(info->input_dev, info); - - info->input_dev->name = "twl6040:vibrator"; - info->input_dev->id.version = 1; - info->input_dev->dev.parent = pdev->dev.parent; - info->input_dev->close = twl6040_vibra_close; - __set_bit(FF_RUMBLE, info->input_dev->ffbit); - - ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); - if (ret < 0) { - dev_err(info->dev, "couldn't register vibrator to FF\n"); - goto err_ialloc; - } - - ret = input_register_device(info->input_dev); - if (ret < 0) { - dev_err(info->dev, "couldn't register input device\n"); - goto err_iff; - } - - platform_set_drvdata(pdev, info); - - ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0, - "twl6040_irq_vib", info); - if (ret) { - dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); - goto err_irq; + error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + twl6040_vib_irq_handler, 0, + "twl6040_irq_vib", info); + if (error) { + dev_err(info->dev, "VIB IRQ request failed: %d\n", error); + return error; } info->supplies[0].supply = "vddvibl"; info->supplies[1].supply = "vddvibr"; - ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies), - info->supplies); - if (ret) { - dev_err(info->dev, "couldn't get regulators %d\n", ret); - goto err_regulator; + /* + * When booted with Device tree the regulators are attached to the + * parent device (twl6040 MFD core) + */ + error = devm_regulator_bulk_get(twl6040_core_dev, + ARRAY_SIZE(info->supplies), + info->supplies); + if (error) { + dev_err(info->dev, "couldn't get regulators %d\n", error); + return error; } - if (pdata->vddvibl_uV) { - ret = regulator_set_voltage(info->supplies[0].consumer, - pdata->vddvibl_uV, - pdata->vddvibl_uV); - if (ret) { + if (vddvibl_uV) { + error = regulator_set_voltage(info->supplies[0].consumer, + vddvibl_uV, vddvibl_uV); + if (error) { dev_err(info->dev, "failed to set VDDVIBL volt %d\n", - ret); - goto err_voltage; + error); + return error; } } - if (pdata->vddvibr_uV) { - ret = regulator_set_voltage(info->supplies[1].consumer, - pdata->vddvibr_uV, - pdata->vddvibr_uV); - if (ret) { + if (vddvibr_uV) { + error = regulator_set_voltage(info->supplies[1].consumer, + vddvibr_uV, vddvibr_uV); + if (error) { dev_err(info->dev, "failed to set VDDVIBR volt %d\n", - ret); - goto err_voltage; + error); + return error; } } - info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); - if (info->workqueue == NULL) { - dev_err(info->dev, "couldn't create workqueue\n"); - ret = -ENOMEM; - goto err_voltage; - } INIT_WORK(&info->play_work, vibra_play_work); - return 0; + info->input_dev = devm_input_allocate_device(&pdev->dev); + if (!info->input_dev) { + dev_err(info->dev, "couldn't allocate input device\n"); + return -ENOMEM; + } -err_voltage: - regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); -err_regulator: - free_irq(info->irq, info); -err_irq: - input_unregister_device(info->input_dev); - info->input_dev = NULL; -err_iff: - if (info->input_dev) - input_ff_destroy(info->input_dev); -err_ialloc: - input_free_device(info->input_dev); -err_kzalloc: - kfree(info); - return ret; -} + input_set_drvdata(info->input_dev, info); -static int __devexit twl6040_vibra_remove(struct platform_device *pdev) -{ - struct vibra_info *info = platform_get_drvdata(pdev); + info->input_dev->name = "twl6040:vibrator"; + info->input_dev->id.version = 1; + info->input_dev->dev.parent = pdev->dev.parent; + info->input_dev->close = twl6040_vibra_close; + __set_bit(FF_RUMBLE, info->input_dev->ffbit); + + error = input_ff_create_memless(info->input_dev, NULL, vibra_play); + if (error) { + dev_err(info->dev, "couldn't register vibrator to FF\n"); + return error; + } + + error = input_register_device(info->input_dev); + if (error) { + dev_err(info->dev, "couldn't register input device\n"); + return error; + } - input_unregister_device(info->input_dev); - free_irq(info->irq, info); - regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); - destroy_workqueue(info->workqueue); - kfree(info); + platform_set_drvdata(pdev, info); return 0; } static struct platform_driver twl6040_vibra_driver = { .probe = twl6040_vibra_probe, - .remove = __devexit_p(twl6040_vibra_remove), .driver = { .name = "twl6040-vibra", .owner = THIS_MODULE, diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 736056897e5..85693624750 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,8 @@ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) + * - add UI_GET_SYSNAME ioctl * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) * - updated ff support for the changes in kernel interface * - added MODULE_VERSION @@ -40,7 +42,8 @@ #include <linux/input/mt.h> #include "../input-compat.h" -static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int uinput_dev_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { struct uinput_device *udev = input_get_drvdata(dev); @@ -56,10 +59,11 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i } /* Atomically allocate an ID for the given request. Returns 0 on success. */ -static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) +static bool uinput_request_alloc_id(struct uinput_device *udev, + struct uinput_request *request) { - int id; - int err = -1; + unsigned int id; + bool reserved = false; spin_lock(&udev->requests_lock); @@ -67,32 +71,35 @@ static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_req if (!udev->requests[id]) { request->id = id; udev->requests[id] = request; - err = 0; + reserved = true; break; } } spin_unlock(&udev->requests_lock); - return err; + return reserved; } -static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) +static struct uinput_request *uinput_request_find(struct uinput_device *udev, + unsigned int id) { /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ - if (id >= UINPUT_NUM_REQUESTS || id < 0) + if (id >= UINPUT_NUM_REQUESTS) return NULL; return udev->requests[id]; } -static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request) +static int uinput_request_reserve_slot(struct uinput_device *udev, + struct uinput_request *request) { /* Allocate slot. If none are available right away, wait. */ return wait_event_interruptible(udev->requests_waitq, - !uinput_request_alloc_id(udev, request)); + uinput_request_alloc_id(udev, request)); } -static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request) +static void uinput_request_done(struct uinput_device *udev, + struct uinput_request *request) { /* Mark slot as available */ udev->requests[request->id] = NULL; @@ -101,14 +108,11 @@ static void uinput_request_done(struct uinput_device *udev, struct uinput_reques complete(&request->done); } -static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) +static int uinput_request_send(struct uinput_device *udev, + struct uinput_request *request) { int retval; - retval = uinput_request_reserve_slot(udev, request); - if (retval) - return retval; - retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; @@ -118,7 +122,12 @@ static int uinput_request_submit(struct uinput_device *udev, struct uinput_reque goto out; } - /* Tell our userspace app about this new request by queueing an input event */ + init_completion(&request->done); + + /* + * Tell our userspace application about this new request + * by queueing an input event. + */ uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); out: @@ -126,8 +135,27 @@ static int uinput_request_submit(struct uinput_device *udev, struct uinput_reque return retval; } +static int uinput_request_submit(struct uinput_device *udev, + struct uinput_request *request) +{ + int error; + + error = uinput_request_reserve_slot(udev, request); + if (error) + return error; + + error = uinput_request_send(udev, request); + if (error) { + uinput_request_done(udev, request); + return error; + } + + wait_for_completion(&request->done); + return request->retval; +} + /* - * Fail all ouitstanding requests so handlers don't wait for the userspace + * Fail all outstanding requests so handlers don't wait for the userspace * to finish processing them. */ static void uinput_flush_requests(struct uinput_device *udev) @@ -163,11 +191,12 @@ static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) return uinput_dev_event(dev, EV_FF, effect_id, value); } -static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) +static int uinput_dev_upload_effect(struct input_dev *dev, + struct ff_effect *effect, + struct ff_effect *old) { struct uinput_device *udev = input_get_drvdata(dev); struct uinput_request request; - int retval; /* * uinput driver does not currently support periodic effects with @@ -180,42 +209,25 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff effect->u.periodic.waveform == FF_CUSTOM) return -EINVAL; - request.id = -1; - init_completion(&request.done); request.code = UI_FF_UPLOAD; request.u.upload.effect = effect; request.u.upload.old = old; - retval = uinput_request_submit(udev, &request); - if (!retval) { - wait_for_completion(&request.done); - retval = request.retval; - } - - return retval; + return uinput_request_submit(udev, &request); } static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) { struct uinput_device *udev = input_get_drvdata(dev); struct uinput_request request; - int retval; if (!test_bit(EV_FF, dev->evbit)) return -ENOSYS; - request.id = -1; - init_completion(&request.done); request.code = UI_FF_ERASE; request.u.effect_id = effect_id; - retval = uinput_request_submit(udev, &request); - if (!retval) { - wait_for_completion(&request.done); - retval = request.retval; - } - - return retval; + return uinput_request_submit(udev, &request); } static void uinput_destroy_device(struct uinput_device *udev) @@ -347,7 +359,8 @@ static int uinput_allocate_device(struct uinput_device *udev) return 0; } -static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count) +static int uinput_setup_device(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct uinput_user_dev *user_dev; struct input_dev *dev; @@ -405,7 +418,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu goto exit; if (test_bit(ABS_MT_SLOT, dev->absbit)) { int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot); + input_mt_init_slots(dev, nslot, 0); } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { input_set_events_per_packet(dev, 60); } @@ -419,32 +432,47 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu return retval; } -static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count) +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; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; - if (input_event_from_user(buffer, &ev)) - return -EFAULT; + while (bytes + input_event_size() <= count) { + /* + * Note that even if some events were fetched successfully + * we are still going to return EFAULT instead of partial + * count to let userspace know that it got it's buffers + * all wrong. + */ + if (input_event_from_user(buffer + bytes, &ev)) + return -EFAULT; - input_event(udev->dev, ev.type, ev.code, ev.value); + input_event(udev->dev, ev.type, ev.code, ev.value); + bytes += input_event_size(); + } - return input_event_size(); + return bytes; } -static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +static ssize_t uinput_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { struct uinput_device *udev = file->private_data; int retval; + if (count == 0) + return 0; + retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; retval = udev->state == UIST_CREATED ? - uinput_inject_event(udev, buffer, count) : + uinput_inject_events(udev, buffer, count) : uinput_setup_device(udev, buffer, count); mutex_unlock(&udev->mutex); @@ -452,42 +480,74 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t return retval; } -static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +static bool uinput_fetch_next_event(struct uinput_device *udev, + struct input_event *event) { - struct uinput_device *udev = file->private_data; - int retval = 0; + bool have_event; - if (udev->state != UIST_CREATED) - return -ENODEV; + spin_lock_irq(&udev->dev->event_lock); - if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; + have_event = udev->head != udev->tail; + if (have_event) { + *event = udev->buff[udev->tail]; + udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; + } - retval = wait_event_interruptible(udev->waitq, - udev->head != udev->tail || udev->state != UIST_CREATED); - if (retval) - return retval; + spin_unlock_irq(&udev->dev->event_lock); - retval = mutex_lock_interruptible(&udev->mutex); - if (retval) - return retval; + return have_event; +} - if (udev->state != UIST_CREATED) { - retval = -ENODEV; - goto out; - } +static ssize_t uinput_events_to_user(struct uinput_device *udev, + char __user *buffer, size_t count) +{ + struct input_event event; + size_t read = 0; - while (udev->head != udev->tail && retval + input_event_size() <= count) { - if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { - retval = -EFAULT; - goto out; - } - udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; - retval += input_event_size(); + while (read + input_event_size() <= count && + uinput_fetch_next_event(udev, &event)) { + + if (input_event_to_user(buffer + read, &event)) + return -EFAULT; + + read += input_event_size(); } - out: - mutex_unlock(&udev->mutex); + return read; +} + +static ssize_t uinput_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct uinput_device *udev = file->private_data; + ssize_t retval; + + if (count != 0 && count < input_event_size()) + return -EINVAL; + + do { + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + if (udev->state != UIST_CREATED) + retval = -ENODEV; + else if (udev->head == udev->tail && + (file->f_flags & O_NONBLOCK)) + retval = -EAGAIN; + else + retval = uinput_events_to_user(udev, buffer, count); + + mutex_unlock(&udev->mutex); + + if (retval || count == 0) + break; + + if (!(file->f_flags & O_NONBLOCK)) + retval = wait_event_interruptible(udev->waitq, + udev->head != udev->tail || + udev->state != UIST_CREATED); + } while (retval == 0); return retval; } @@ -516,8 +576,8 @@ static int uinput_release(struct inode *inode, struct file *file) #ifdef CONFIG_COMPAT struct uinput_ff_upload_compat { - int request_id; - int retval; + __u32 request_id; + __s32 retval; struct ff_effect_compat effect; struct ff_effect_compat old; }; @@ -612,6 +672,31 @@ static int uinput_ff_upload_from_user(const char __user *buffer, __ret; \ }) +static int uinput_str_to_user(void __user *dest, const char *str, + unsigned int maxlen) +{ + char __user *p = dest; + int len, ret; + + if (!str) + return -ENOENT; + + if (maxlen == 0) + return -EINVAL; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + ret = copy_to_user(p, str, len); + if (ret) + return -EFAULT; + + /* force terminating '\0' */ + ret = put_user(0, p + len - 1); + return ret ? -EFAULT : len; +} + static long uinput_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { @@ -621,6 +706,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, struct uinput_ff_erase ff_erase; struct uinput_request *req; char *phys; + const char *name; + unsigned int size; retval = mutex_lock_interruptible(&udev->mutex); if (retval) @@ -635,51 +722,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, switch (cmd) { case UI_DEV_CREATE: retval = uinput_create_device(udev); - break; + goto out; case UI_DEV_DESTROY: uinput_destroy_device(udev); - break; + goto out; case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); - break; + goto out; case UI_SET_KEYBIT: retval = uinput_set_bit(arg, keybit, KEY_MAX); - break; + goto out; case UI_SET_RELBIT: retval = uinput_set_bit(arg, relbit, REL_MAX); - break; + goto out; case UI_SET_ABSBIT: retval = uinput_set_bit(arg, absbit, ABS_MAX); - break; + goto out; case UI_SET_MSCBIT: retval = uinput_set_bit(arg, mscbit, MSC_MAX); - break; + goto out; case UI_SET_LEDBIT: retval = uinput_set_bit(arg, ledbit, LED_MAX); - break; + goto out; case UI_SET_SNDBIT: retval = uinput_set_bit(arg, sndbit, SND_MAX); - break; + goto out; case UI_SET_FFBIT: retval = uinput_set_bit(arg, ffbit, FF_MAX); - break; + goto out; case UI_SET_SWBIT: retval = uinput_set_bit(arg, swbit, SW_MAX); - break; + goto out; case UI_SET_PROPBIT: retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); - break; + goto out; case UI_SET_PHYS: if (udev->state == UIST_CREATED) { @@ -695,17 +782,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, kfree(udev->dev->phys); udev->dev->phys = phys; - break; + goto out; case UI_BEGIN_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); - if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { + if (!req || req->code != UI_FF_UPLOAD || + !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } ff_up.retval = 0; @@ -716,65 +804,77 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, memset(&ff_up.old, 0, sizeof(struct ff_effect)); retval = uinput_ff_upload_to_user(p, &ff_up); - break; + goto out; case UI_BEGIN_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } ff_erase.retval = 0; ff_erase.effect_id = req->u.effect_id; if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } - break; + goto out; case UI_END_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } req->retval = ff_up.retval; uinput_request_done(udev, req); - break; + goto out; case UI_END_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } req->retval = ff_erase.retval; uinput_request_done(udev, req); - break; + goto out; + } - default: - retval = -EINVAL; + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ + switch (cmd & ~IOCSIZE_MASK) { + case UI_GET_SYSNAME(0): + if (udev->state != UIST_CREATED) { + retval = -ENOENT; + goto out; + } + name = dev_name(&udev->dev->dev); + retval = uinput_str_to_user(p, name, size); + goto out; } + retval = -EINVAL; out: mutex_unlock(&udev->mutex); return retval; @@ -786,7 +886,8 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT -static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long uinput_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) { return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); } @@ -831,4 +932,3 @@ MODULE_VERSION("0.3"); module_init(uinput_init); module_exit(uinput_exit); - diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index e2bdfd4bea7..7b7add5061a 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -46,7 +46,6 @@ MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); MODULE_DESCRIPTION("Wistron laptop button driver"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.3"); static bool force; /* = 0; */ module_param(force, bool, 0); @@ -170,7 +169,7 @@ static u16 bios_pop_queue(void) return regs.eax; } -static void __devinit bios_attach(void) +static void bios_attach(void) { struct regs regs; @@ -190,7 +189,7 @@ static void bios_detach(void) call_bios(®s); } -static u8 __devinit bios_get_cmos_address(void) +static u8 bios_get_cmos_address(void) { struct regs regs; @@ -202,7 +201,7 @@ static u8 __devinit bios_get_cmos_address(void) return regs.ecx; } -static u16 __devinit bios_get_default_setting(u8 subsys) +static u16 bios_get_default_setting(u8 subsys) { struct regs regs; @@ -278,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { { KE_END, 0 } }; +static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */ + { 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_END, FE_WIFI_LED } +}; + static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, {KEY_PROG1} }, { KE_KEY, 0x12, {KEY_PROG2} }, @@ -563,7 +572,7 @@ static struct key_entry keymap_wistron_md96500[] __initdata = { { KE_KEY, 0x36, {KEY_WWW} }, { KE_WIFI, 0x30 }, { KE_BLUETOOTH, 0x44 }, - { KE_END, FE_UNTESTED } + { KE_END, 0 } }; static struct key_entry keymap_wistron_generic[] __initdata = { @@ -635,7 +644,7 @@ static struct key_entry keymap_prestigio[] __initdata = { * a list of buttons and their key codes (reported when loading this module * with force=1) and the output of dmidecode to $MODULE_AUTHOR. */ -static const struct dmi_system_id __initconst dmi_ids[] = { +static const struct dmi_system_id dmi_ids[] __initconst = { { /* Fujitsu-Siemens Amilo Pro V2000 */ .callback = dmi_matched, @@ -655,6 +664,15 @@ static const struct dmi_system_id __initconst dmi_ids[] = { .driver_data = keymap_fs_amilo_pro_v3505 }, { + /* Fujitsu-Siemens Amilo Pro Edition V8210 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Series V8210"), + }, + .driver_data = keymap_fs_amilo_pro_v8210 + }, + { /* Fujitsu-Siemens Amilo M7400 */ .callback = dmi_matched, .matches = { @@ -972,6 +990,7 @@ static const struct dmi_system_id __initconst dmi_ids[] = { }, { NULL, } }; +MODULE_DEVICE_TABLE(dmi, dmi_ids); /* Copy the good keymap, as the original ones are free'd */ static int __init copy_keymap(void) @@ -1052,7 +1071,7 @@ static struct led_classdev wistron_wifi_led = { .brightness_set = wistron_wifi_led_set, }; -static void __devinit wistron_led_init(struct device *parent) +static void wistron_led_init(struct device *parent) { if (leds_present & FE_WIFI_LED) { u16 wifi = bios_get_default_setting(WIFI); @@ -1077,7 +1096,7 @@ static void __devinit wistron_led_init(struct device *parent) } } -static void __devexit wistron_led_remove(void) +static void wistron_led_remove(void) { if (leds_present & FE_MAIL_LED) led_classdev_unregister(&wistron_mail_led); @@ -1168,7 +1187,7 @@ static void wistron_poll(struct input_polled_dev *dev) dev->poll_interval = POLL_INTERVAL_DEFAULT; } -static int __devinit wistron_setup_keymap(struct input_dev *dev, +static int wistron_setup_keymap(struct input_dev *dev, struct key_entry *entry) { switch (entry->type) { @@ -1199,7 +1218,7 @@ static int __devinit wistron_setup_keymap(struct input_dev *dev, return 0; } -static int __devinit setup_input_dev(void) +static int setup_input_dev(void) { struct input_dev *input_dev; int error; @@ -1237,7 +1256,7 @@ static int __devinit setup_input_dev(void) /* Driver core */ -static int __devinit wistron_probe(struct platform_device *dev) +static int wistron_probe(struct platform_device *dev) { int err; @@ -1277,7 +1296,7 @@ static int __devinit wistron_probe(struct platform_device *dev) return 0; } -static int __devexit wistron_remove(struct platform_device *dev) +static int wistron_remove(struct platform_device *dev) { wistron_led_remove(); input_unregister_polled_device(wistron_idev); @@ -1334,7 +1353,7 @@ static struct platform_driver wistron_driver = { #endif }, .probe = wistron_probe, - .remove = __devexit_p(wistron_remove), + .remove = wistron_remove, }; static int __init wb_module_init(void) diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index 47f18d6bce4..173b6dcca0d 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -18,7 +18,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -69,14 +68,15 @@ static irqreturn_t wm831x_on_irq(int irq, void *data) return IRQ_HANDLED; } -static int __devinit wm831x_on_probe(struct platform_device *pdev) +static int wm831x_on_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_on *wm831x_on; - int irq = platform_get_irq(pdev, 0); + int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); int ret; - wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); + wm831x_on = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_on), + GFP_KERNEL); if (!wm831x_on) { dev_err(&pdev->dev, "Can't allocate data\n"); return -ENOMEM; @@ -85,7 +85,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev) wm831x_on->wm831x = wm831x; INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); - wm831x_on->dev = input_allocate_device(); + wm831x_on->dev = devm_input_allocate_device(&pdev->dev); if (!wm831x_on->dev) { dev_err(&pdev->dev, "Can't allocate input dev\n"); ret = -ENOMEM; @@ -118,28 +118,24 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev) err_irq: free_irq(irq, wm831x_on); err_input_dev: - input_free_device(wm831x_on->dev); err: - kfree(wm831x_on); return ret; } -static int __devexit wm831x_on_remove(struct platform_device *pdev) +static int 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); - input_unregister_device(wm831x_on->dev); - kfree(wm831x_on); return 0; } static struct platform_driver wm831x_on_driver = { .probe = wm831x_on_probe, - .remove = __devexit_p(wm831x_on_remove), + .remove = wm831x_on_remove, .driver = { .name = "wm831x-on", .owner = THIS_MODULE, diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 02ca8680ea5..fbfdc10573b 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -29,6 +29,7 @@ #include <xen/interface/io/fbif.h> #include <xen/interface/io/kbdif.h> #include <xen/xenbus.h> +#include <xen/platform_pci.h> struct xenkbd_info { struct input_dev *kbd; @@ -104,7 +105,7 @@ static irqreturn_t input_handler(int rq, void *dev_id) return IRQ_HANDLED; } -static int __devinit xenkbd_probe(struct xenbus_device *dev, +static int xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { int ret, i, abs; @@ -311,7 +312,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateReconfiguring: case XenbusStateReconfigured: case XenbusStateUnknown: - case XenbusStateClosed: break; case XenbusStateInitWait: @@ -350,6 +350,10 @@ InitWait: break; + case XenbusStateClosed: + if (dev->state == XenbusStateClosed) + break; + /* Missed the backend's CLOSING state -- fallthrough */ case XenbusStateClosing: xenbus_frontend_closed(dev); break; @@ -377,6 +381,9 @@ static int __init xenkbd_init(void) if (xen_initial_domain()) return -ENODEV; + if (!xen_has_pv_devices()) + return -ENODEV; + return xenbus_register_frontend(&xenkbd_driver); } diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index f4776e7f8c1..79c964c075f 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -47,7 +47,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/rwsem.h> @@ -101,6 +100,7 @@ static const struct lcd_segment_map { struct yealink_dev { struct input_dev *idev; /* input device */ struct usb_device *udev; /* usb device */ + struct usb_interface *intf; /* usb interface */ /* irq input channel */ struct yld_ctl_packet *irq_data; @@ -428,7 +428,8 @@ static void urb_irq_callback(struct urb *urb) int ret, status = urb->status; if (status) - err("%s - urb status %d", __func__, status); + dev_err(&yld->intf->dev, "%s - urb status %d\n", + __func__, status); switch (yld->irq_data->cmd) { case CMD_KEYPRESS: @@ -437,13 +438,15 @@ static void urb_irq_callback(struct urb *urb) break; case CMD_SCANCODE: - dbg("get scancode %x", yld->irq_data->data[0]); + dev_dbg(&yld->intf->dev, "get scancode %x\n", + yld->irq_data->data[0]); report_key(yld, map_p1k_to_key(yld->irq_data->data[0])); break; default: - err("unexpected response %x", yld->irq_data->cmd); + dev_err(&yld->intf->dev, "unexpected response %x\n", + yld->irq_data->cmd); } yealink_do_idle_tasks(yld); @@ -451,7 +454,9 @@ static void urb_irq_callback(struct urb *urb) if (!yld->shutdown) { ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); if (ret && ret != -EPERM) - err("%s - usb_submit_urb failed %d", __func__, ret); + dev_err(&yld->intf->dev, + "%s - usb_submit_urb failed %d\n", + __func__, ret); } } @@ -461,7 +466,8 @@ static void urb_ctl_callback(struct urb *urb) int ret = 0, status = urb->status; if (status) - err("%s - urb status %d", __func__, status); + dev_err(&yld->intf->dev, "%s - urb status %d\n", + __func__, status); switch (yld->ctl_data->cmd) { case CMD_KEYPRESS: @@ -479,7 +485,8 @@ static void urb_ctl_callback(struct urb *urb) } if (ret && ret != -EPERM) - err("%s - usb_submit_urb failed %d", __func__, ret); + dev_err(&yld->intf->dev, "%s - usb_submit_urb failed %d\n", + __func__, ret); } /******************************************************************************* @@ -511,7 +518,7 @@ static int input_open(struct input_dev *dev) struct yealink_dev *yld = input_get_drvdata(dev); int i, ret; - dbg("%s", __func__); + dev_dbg(&yld->intf->dev, "%s\n", __func__); /* force updates to device */ for (i = 0; i<sizeof(yld->master); i++) @@ -526,8 +533,9 @@ static int input_open(struct input_dev *dev) yld->ctl_data->size = 10; yld->ctl_data->sum = 0x100-CMD_INIT-10; if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) { - dbg("%s - usb_submit_urb failed with result %d", - __func__, ret); + dev_dbg(&yld->intf->dev, + "%s - usb_submit_urb failed with result %d\n", + __func__, ret); return ret; } return 0; @@ -876,6 +884,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) return -ENOMEM; yld->udev = udev; + yld->intf = intf; yld->idev = input_dev = input_allocate_device(); if (!input_dev) @@ -909,7 +918,8 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); if (ret != USB_PKT_LEN) - err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN); + dev_err(&intf->dev, "invalid payload size %d, expected %zd\n", + ret, USB_PKT_LEN); /* initialise irq urb */ usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data, |
