summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-01 13:20:04 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-01 13:20:04 -0700
commit251df49db3327c64bf917bfdba94491fde2b4ee0 (patch)
tree71eef72e1c393057f7b14cc4d8da5e48c7728336
parent8a72f3820c4d14b27ad5336aed00063a7a7f1bef (diff)
parentbf61c8840efe60fd8f91446860b63338fb424158 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "Assorted fixes and cleanups to the existing drivers plus a new driver for IMS Passenger Control Unit device they use for ther in-flight entertainment system." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (44 commits) Input: trackpoint - Optimize trackpoint init to use power-on reset Input: apbps2 - convert to devm_ioremap_resource() Input: ALPS - use %ph to print buffers ARM - shmobile: Armadillo800EVA: Move st1232 reset pin handling Input: st1232 - add reset pin handling Input: st1232 - convert to devm_* infrastructure Input: MT - handle semi-mt devices in core Input: adxl34x - use spi_get_drvdata() Input: ad7877 - use spi_get_drvdata() and spi_set_drvdata() Input: ads7846 - use spi_get_drvdata() and spi_set_drvdata() Input: ims-pcu - fix a memory leak on error Input: sysrq - supplement reset sequence with timeout functionality Input: tegra-kbc - support for defining row/columns based on SoC Input: imx_keypad - switch to using managed resources Input: arc_ps2 - add support for device tree Input: mma8450 - fix signed 12bits to 32bits conversion Input: eeti_ts - remove redundant null check Input: edt-ft5x06 - remove redundant null check before kfree Input: ad714x - add CONFIG_PM_SLEEP to suspend/resume functions Input: adxl34x - add CONFIG_PM_SLEEP to suspend/resume functions ...
-rw-r--r--Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt16
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt30
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt24
-rw-r--r--Documentation/devicetree/bindings/serio/snps-arc_ps2.txt16
-rw-r--r--arch/arm/mach-shmobile/board-armadillo800eva.c7
-rw-r--r--drivers/base/devres.c74
-rw-r--r--drivers/input/input-mt.c8
-rw-r--r--drivers/input/keyboard/amikbd.c14
-rw-r--r--drivers/input/keyboard/davinci_keyscan.c12
-rw-r--r--drivers/input/keyboard/imx_keypad.c77
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c12
-rw-r--r--drivers/input/keyboard/tegra-kbc.c89
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ad714x-i2c.c2
-rw-r--r--drivers/input/misc/ad714x-spi.c2
-rw-r--r--drivers/input/misc/adxl34x-i2c.c2
-rw-r--r--drivers/input/misc/adxl34x-spi.c8
-rw-r--r--drivers/input/misc/ims-pcu.c1901
-rw-r--r--drivers/input/misc/mma8450.c6
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c12
-rw-r--r--drivers/input/mouse/alps.c12
-rw-r--r--drivers/input/mouse/amimouse.c14
-rw-r--r--drivers/input/mouse/trackpoint.c249
-rw-r--r--drivers/input/mouse/trackpoint.h4
-rw-r--r--drivers/input/serio/Kconfig10
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/apbps2.c228
-rw-r--r--drivers/input/serio/arc_ps2.c14
-rw-r--r--drivers/input/serio/at32psif.c13
-rw-r--r--drivers/input/serio/q40kbd.c13
-rw-r--r--drivers/input/touchscreen/ad7877.c10
-rw-r--r--drivers/input/touchscreen/ads7846.c4
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c12
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c226
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c3
-rw-r--r--drivers/input/touchscreen/eeti_ts.c3
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c12
-rw-r--r--drivers/input/touchscreen/st1232.c77
-rw-r--r--drivers/input/touchscreen/wm9712.c28
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c11
-rw-r--r--drivers/tty/sysrq.c53
-rw-r--r--drivers/usb/class/cdc-acm.c13
-rw-r--r--drivers/usb/class/cdc-acm.h1
-rw-r--r--include/linux/device.h4
-rw-r--r--include/linux/input/auo-pixcir-ts.h4
-rw-r--r--include/linux/input/mt.h1
-rw-r--r--include/linux/platform_data/st1232_pdata.h13
-rw-r--r--include/uapi/linux/input.h5
49 files changed, 2917 insertions, 444 deletions
diff --git a/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
new file mode 100644
index 00000000000..3029c5694cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
@@ -0,0 +1,16 @@
+Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse.
+
+The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library.
+
+Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system,
+these properties are built from information in the AMBA plug&play and from
+bootloader settings.
+
+Required properties:
+
+- name : Should be "GAISLER_APBPS2" or "01_060"
+- reg : Address and length of the register set for the device
+- interrupts : Interrupt numbers for this device
+
+For further information look in the documentation for the GLIB IP core library:
+http://www.gaisler.com/products/grlib/grip.pdf
diff --git a/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt
new file mode 100644
index 00000000000..f40f21c642b
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt
@@ -0,0 +1,30 @@
+* AUO in-cell touchscreen controller using Pixcir sensors
+
+Required properties:
+- compatible: must be "auo,auo_pixcir_ts"
+- reg: I2C address of the chip
+- interrupts: interrupt to which the chip is connected
+- gpios: gpios the chip is connected to
+ first one is the interrupt gpio and second one the reset gpio
+- x-size: horizontal resolution of touchscreen
+- y-size: vertical resolution of touchscreen
+
+Example:
+
+ i2c@00000000 {
+ /* ... */
+
+ auo_pixcir_ts@5c {
+ compatible = "auo,auo_pixcir_ts";
+ reg = <0x5c>;
+ interrupts = <2 0>;
+
+ gpios = <&gpf 2 0 2>, /* INT */
+ <&gpf 5 1 0>; /* RST */
+
+ x-size = <800>;
+ y-size = <600>;
+ };
+
+ /* ... */
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
new file mode 100644
index 00000000000..64ad48b824a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
@@ -0,0 +1,24 @@
+* Sitronix st1232 touchscreen controller
+
+Required properties:
+- compatible: must be "sitronix,st1232"
+- reg: I2C address of the chip
+- interrupts: interrupt to which the chip is connected
+
+Optional properties:
+- gpios: a phandle to the reset GPIO
+
+Example:
+
+ i2c@00000000 {
+ /* ... */
+
+ touchscreen@55 {
+ compatible = "sitronix,st1232";
+ reg = <0x55>;
+ interrupts = <2 0>;
+ gpios = <&gpio1 166 0>;
+ };
+
+ /* ... */
+ };
diff --git a/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt b/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt
new file mode 100644
index 00000000000..38c2f21e804
--- /dev/null
+++ b/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt
@@ -0,0 +1,16 @@
+* ARC PS/2 driver: PS/2 block used in some ARC FPGA's & nSIM OSCI model
+
+Required properties:
+- compatible : "snps,arc_ps2"
+- reg : offset and length (always 0x14) of registers
+- interrupts : interrupt
+- interrupt-names : name of interrupt, must be "arc_ps2_irq"
+
+Example:
+
+serio@c9000400 {
+ compatible = "snps,arc_ps2";
+ reg = <0xc9000400 0x14>;
+ interrupts = <13>;
+ interrupt-names = "arc_ps2_irq";
+}
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c
index ff8b7ba9b93..881e5c0e41d 100644
--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
+++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
@@ -24,6 +24,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/input.h>
+#include <linux/platform_data/st1232_pdata.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
@@ -882,10 +883,15 @@ static struct platform_device i2c_gpio_device = {
};
/* I2C */
+static struct st1232_pdata st1232_i2c0_pdata = {
+ .reset_gpio = 166,
+};
+
static struct i2c_board_info i2c0_devices[] = {
{
I2C_BOARD_INFO("st1232-ts", 0x55),
.irq = evt2irq(0x0340),
+ .platform_data = &st1232_i2c0_pdata,
},
{
I2C_BOARD_INFO("wm8978", 0x1a),
@@ -1009,7 +1015,6 @@ static void __init eva_init(void)
/* Touchscreen */
gpio_request(GPIO_FN_IRQ10, NULL); /* TP_INT */
- gpio_request_one(GPIO_PORT166, GPIOF_OUT_INIT_HIGH, NULL); /* TP_RST_B */
/* GETHER */
gpio_request(GPIO_FN_ET_CRS, NULL);
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 66839066476..507379e7b76 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -671,6 +671,80 @@ int devres_release_group(struct device *dev, void *id)
EXPORT_SYMBOL_GPL(devres_release_group);
/*
+ * Custom devres actions allow inserting a simple function call
+ * into the teadown sequence.
+ */
+
+struct action_devres {
+ void *data;
+ void (*action)(void *);
+};
+
+static int devm_action_match(struct device *dev, void *res, void *p)
+{
+ struct action_devres *devres = res;
+ struct action_devres *target = p;
+
+ return devres->action == target->action &&
+ devres->data == target->data;
+}
+
+static void devm_action_release(struct device *dev, void *res)
+{
+ struct action_devres *devres = res;
+
+ devres->action(devres->data);
+}
+
+/**
+ * devm_add_action() - add a custom action to list of managed resources
+ * @dev: Device that owns the action
+ * @action: Function that should be called
+ * @data: Pointer to data passed to @action implementation
+ *
+ * This adds a custom action to the list of managed resources so that
+ * it gets executed as part of standard resource unwinding.
+ */
+int devm_add_action(struct device *dev, void (*action)(void *), void *data)
+{
+ struct action_devres *devres;
+
+ devres = devres_alloc(devm_action_release,
+ sizeof(struct action_devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ devres->data = data;
+ devres->action = action;
+
+ devres_add(dev, devres);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_add_action);
+
+/**
+ * devm_remove_action() - removes previously added custom action
+ * @dev: Device that owns the action
+ * @action: Function implementing the action
+ * @data: Pointer to data passed to @action implementation
+ *
+ * Removes instance of @action previously added by devm_add_action().
+ * Both action and data should match one of the existing entries.
+ */
+void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
+{
+ struct action_devres devres = {
+ .data = data,
+ .action = action,
+ };
+
+ WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
+ &devres));
+
+}
+EXPORT_SYMBOL_GPL(devm_remove_action);
+
+/*
* Managed kzalloc/kfree
*/
static void devm_kzalloc_release(struct device *dev, void *res)
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 71db1930573..d398f1321f1 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -79,6 +79,8 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
}
if (flags & INPUT_MT_DIRECT)
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
+ if (flags & INPUT_MT_SEMI_MT)
+ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
if (flags & INPUT_MT_TRACK) {
unsigned int n2 = num_slots * num_slots;
mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
@@ -246,6 +248,7 @@ void input_mt_sync_frame(struct input_dev *dev)
{
struct input_mt *mt = dev->mt;
struct input_mt_slot *s;
+ bool use_count = false;
if (!mt)
return;
@@ -259,7 +262,10 @@ void input_mt_sync_frame(struct input_dev *dev)
}
}
- input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
+ if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT))
+ use_count = true;
+
+ input_mt_report_pointer_emulation(dev, use_count);
mt->frame++;
}
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
index 79172af164f..ba0b36f7dae 100644
--- a/drivers/input/keyboard/amikbd.c
+++ b/drivers/input/keyboard/amikbd.c
@@ -260,18 +260,6 @@ static struct platform_driver amikbd_driver = {
},
};
-static int __init amikbd_init(void)
-{
- return platform_driver_probe(&amikbd_driver, amikbd_probe);
-}
-
-module_init(amikbd_init);
-
-static void __exit amikbd_exit(void)
-{
- platform_driver_unregister(&amikbd_driver);
-}
-
-module_exit(amikbd_exit);
+module_platform_driver_probe(amikbd_driver, amikbd_probe);
MODULE_ALIAS("platform:amiga-keyboard");
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c
index 4e4e453ea15..829753702b6 100644
--- a/drivers/input/keyboard/davinci_keyscan.c
+++ b/drivers/input/keyboard/davinci_keyscan.c
@@ -329,17 +329,7 @@ static struct platform_driver davinci_ks_driver = {
.remove = davinci_ks_remove,
};
-static int __init davinci_ks_init(void)
-{
- return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe);
-}
-module_init(davinci_ks_init);
-
-static void __exit davinci_ks_exit(void)
-{
- platform_driver_unregister(&davinci_ks_driver);
-}
-module_exit(davinci_ks_exit);
+module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index 98f9113251d..03c8cc5cb6c 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -448,24 +448,17 @@ static int imx_keypad_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = request_mem_region(res->start, resource_size(res), pdev->name);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to request I/O memory\n");
- return -EBUSY;
- }
-
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
- error = -ENOMEM;
- goto failed_rel_mem;
+ return -ENOMEM;
}
- keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL);
+ keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
+ GFP_KERNEL);
if (!keypad) {
dev_err(&pdev->dev, "not enough memory for driver data\n");
- error = -ENOMEM;
- goto failed_free_input;
+ return -ENOMEM;
}
keypad->input_dev = input_dev;
@@ -475,18 +468,14 @@ static int imx_keypad_probe(struct platform_device *pdev)
setup_timer(&keypad->check_matrix_timer,
imx_keypad_check_for_events, (unsigned long) keypad);
- keypad->mmio_base = ioremap(res->start, resource_size(res));
- if (keypad->mmio_base == NULL) {
- dev_err(&pdev->dev, "failed to remap I/O memory\n");
- error = -ENOMEM;
- goto failed_free_priv;
- }
+ keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(keypad->mmio_base))
+ return PTR_ERR(keypad->mmio_base);
- keypad->clk = clk_get(&pdev->dev, NULL);
+ keypad->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
- error = PTR_ERR(keypad->clk);
- goto failed_unmap;
+ return PTR_ERR(keypad->clk);
}
/* Init the Input device */
@@ -502,7 +491,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
keypad->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
- goto failed_clock_put;
+ return error;
}
/* Search for rows and cols enabled */
@@ -527,61 +516,24 @@ static int imx_keypad_probe(struct platform_device *pdev)
imx_keypad_inhibit(keypad);
clk_disable_unprepare(keypad->clk);
- error = request_irq(irq, imx_keypad_irq_handler, 0,
+ error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
- goto failed_clock_put;
+ return error;
}
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
- goto failed_free_irq;
+ return error;
}
platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1);
return 0;
-
-failed_free_irq:
- free_irq(irq, pdev);
-failed_clock_put:
- clk_put(keypad->clk);
-failed_unmap:
- iounmap(keypad->mmio_base);
-failed_free_priv:
- kfree(keypad);
-failed_free_input:
- input_free_device(input_dev);
-failed_rel_mem:
- release_mem_region(res->start, resource_size(res));
- return error;
-}
-
-static int imx_keypad_remove(struct platform_device *pdev)
-{
- struct imx_keypad *keypad = platform_get_drvdata(pdev);
- struct resource *res;
-
- dev_dbg(&pdev->dev, ">%s\n", __func__);
-
- platform_set_drvdata(pdev, NULL);
-
- input_unregister_device(keypad->input_dev);
-
- free_irq(keypad->irq, keypad);
- clk_put(keypad->clk);
-
- iounmap(keypad->mmio_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- kfree(keypad);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -640,7 +592,6 @@ static struct platform_driver imx_keypad_driver = {
.of_match_table = of_match_ptr(imx_keypad_of_match),
},
.probe = imx_keypad_probe,
- .remove = imx_keypad_remove,
};
module_platform_driver(imx_keypad_driver);
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index 0e6a8151fee..c7d505cce72 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -430,17 +430,7 @@ static struct platform_driver ske_keypad_driver = {
.remove = ske_keypad_remove,
};
-static int __init ske_keypad_init(void)
-{
- return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
-}
-module_init(ske_keypad_init);
-
-static void __exit ske_keypad_exit(void)
-{
- platform_driver_unregister(&ske_keypad_driver);
-}
-module_exit(ske_keypad_exit);
+module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 0e138ebcc76..b46142f78ef 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -27,17 +27,19 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/input/matrix_keypad.h>
#include <linux/clk/tegra.h>
+#include <linux/err.h>
-#define KBC_MAX_GPIO 24
#define KBC_MAX_KPENT 8
-#define KBC_MAX_ROW 16
-#define KBC_MAX_COL 8
-#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL)
+/* Maximum row/column supported by Tegra KBC yet is 16x8 */
+#define KBC_MAX_GPIO 24
+/* Maximum keys supported by Tegra KBC yet is 16 x 8*/
+#define KBC_MAX_KEY (16 * 8)
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu
@@ -80,6 +82,12 @@ enum tegra_pin_type {
PIN_CFG_ROW,
};
+/* Tegra KBC hw support */
+struct tegra_kbc_hw_support {
+ int max_rows;
+ int max_columns;
+};
+
struct tegra_kbc_pin_cfg {
enum tegra_pin_type type;
unsigned char num;
@@ -108,6 +116,9 @@ struct tegra_kbc {
u32 wakeup_key;
struct timer_list timer;
struct clk *clk;
+ const struct tegra_kbc_hw_support *hw_support;
+ int max_keys;
+ int num_rows_and_columns;
};
static void tegra_kbc_report_released_keys(struct input_dev *input,
@@ -204,11 +215,11 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
/*
* If the platform uses Fn keymaps, translate keys on a Fn keypress.
- * Function keycodes are KBC_MAX_KEY apart from the plain keycodes.
+ * Function keycodes are max_keys apart from the plain keycodes.
*/
if (fn_keypress) {
for (i = 0; i < num_down; i++) {
- scancodes[i] += KBC_MAX_KEY;
+ scancodes[i] += kbc->max_keys;
keycodes[i] = kbc->keycode[scancodes[i]];
}
}
@@ -315,7 +326,7 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
/* Either mask all keys or none. */
rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
- for (i = 0; i < KBC_MAX_ROW; i++)
+ for (i = 0; i < kbc->hw_support->max_rows; i++)
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
}
@@ -452,7 +463,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
switch (pin_cfg->type) {
case PIN_CFG_ROW:
- if (pin_cfg->num >= KBC_MAX_ROW) {
+ if (pin_cfg->num >= kbc->hw_support->max_rows) {
dev_err(kbc->dev,
"pin_cfg[%d]: invalid row number %d\n",
i, pin_cfg->num);
@@ -462,7 +473,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
break;
case PIN_CFG_COL:
- if (pin_cfg->num >= KBC_MAX_COL) {
+ if (pin_cfg->num >= kbc->hw_support->max_columns) {
dev_err(kbc->dev,
"pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num);
@@ -520,6 +531,18 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
}
num_cols = proplen / sizeof(u32);
+ if (num_rows > kbc->hw_support->max_rows) {
+ dev_err(kbc->dev,
+ "Number of rows is more than supported by hardware\n");
+ return -EINVAL;
+ }
+
+ if (num_cols > kbc->hw_support->max_columns) {
+ dev_err(kbc->dev,
+ "Number of cols is more than supported by hardware\n");
+ return -EINVAL;
+ }
+
if (!of_get_property(np, "linux,keymap", &proplen)) {
dev_err(kbc->dev, "property linux,keymap not found\n");
return -ENOENT;
@@ -532,7 +555,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
}
/* Set all pins as non-configured */
- for (i = 0; i < KBC_MAX_GPIO; i++)
+ for (i = 0; i < kbc->num_rows_and_columns; i++)
kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
@@ -562,6 +585,24 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
return 0;
}
+static const struct tegra_kbc_hw_support tegra20_kbc_hw_support = {
+ .max_rows = 16,
+ .max_columns = 8,
+};
+
+static const struct tegra_kbc_hw_support tegra11_kbc_hw_support = {
+ .max_rows = 11,
+ .max_columns = 8,
+};
+
+static const struct of_device_id tegra_kbc_of_match[] = {
+ { .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support},
+ { .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support},
+ { .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support},
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
+
static int tegra_kbc_probe(struct platform_device *pdev)
{
struct tegra_kbc *kbc;
@@ -570,7 +611,10 @@ static int tegra_kbc_probe(struct platform_device *pdev)
int num_rows = 0;
unsigned int debounce_cnt;
unsigned int scan_time_rows;
- unsigned int keymap_rows = KBC_MAX_KEY;
+ unsigned int keymap_rows;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(tegra_kbc_of_match), &pdev->dev);
kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
if (!kbc) {
@@ -579,6 +623,12 @@ static int tegra_kbc_probe(struct platform_device *pdev)
}
kbc->dev = &pdev->dev;
+ kbc->hw_support = match->data;
+ kbc->max_keys = kbc->hw_support->max_rows *
+ kbc->hw_support->max_columns;
+ kbc->num_rows_and_columns = kbc->hw_support->max_rows +
+ kbc->hw_support->max_columns;
+ keymap_rows = kbc->max_keys;
spin_lock_init(&kbc->lock);
err = tegra_kbc_parse_dt(kbc);
@@ -608,11 +658,9 @@ static int tegra_kbc_probe(struct platform_device *pdev)
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
- kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
- if (!kbc->mmio) {
- dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
- return -EBUSY;
- }
+ kbc->mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(kbc->mmio))
+ return PTR_ERR(kbc->mmio);
kbc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbc->clk)) {
@@ -641,7 +689,8 @@ static int tegra_kbc_probe(struct platform_device *pdev)
keymap_rows *= 2;
err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
- keymap_rows, KBC_MAX_COL,
+ keymap_rows,
+ kbc->hw_support->max_columns,
kbc->keycode, kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to setup keymap\n");
@@ -767,12 +816,6 @@ static int tegra_kbc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume);
-static const struct of_device_id tegra_kbc_of_match[] = {
- { .compatible = "nvidia,tegra20-kbc", },
- { },
-};
-MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
-
static struct platform_driver tegra_kbc_driver = {
.probe = tegra_kbc_probe,
.driver = {
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 259ef31abb1..af80928a46b 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -590,6 +590,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
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1f1e1b109d9..d7fc17f11d7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.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
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index 29d2064c26f..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)));
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index bdccca42d13..61891486067 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)));
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index 535dda48cac..416f47ddcc9 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -105,7 +105,7 @@ static int adxl34x_i2c_remove(struct i2c_client *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);
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
index ad5f40d37e4..76dc0679d3b 100644
--- a/drivers/input/misc/adxl34x-spi.c
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -89,16 +89,16 @@ static int adxl34x_spi_probe(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);
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
new file mode 100644
index 00000000000..e204f26b001
--- /dev/null
+++ b/drivers/input/misc/ims-pcu.c
@@ -0,0 +1,1901 @@
+/*
+ * 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_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;
+
+ 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 */
+
+/* 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_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,
+};
+
+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");
+ 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;
+ u8 device_id;
+ 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, &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 (device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
+ !ims_pcu_device_info[device_id].keymap) {
+ dev_err(pcu->dev, "Device ID %d is not valid\n", 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;
+
+ error = ims_pcu_setup_backlight(pcu);
+ if (error)
+ return error;
+
+ info = &ims_pcu_device_info[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_backlight:
+ ims_pcu_destroy_backlight(pcu);
+err_destroy_buttons:
+ ims_pcu_destroy_buttons(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);
+ }
+}
+
+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/mma8450.c b/drivers/input/misc/mma8450.c
index 480557f14f2..f3309696d05 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);
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index 27c2bc8aa89..b9a05fda03e 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -114,18 +114,8 @@ static struct platform_driver twl4030_pwrbutton_driver = {
},
};
-static int __init twl4030_pwrbutton_init(void)
-{
- return platform_driver_probe(&twl4030_pwrbutton_driver,
+module_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_ALIAS("platform:twl4030_pwrbutton");
MODULE_DESCRIPTION("Triton2 Power Button");
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 0238e0e1433..7c5d72a6a26 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1013,8 +1013,8 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -EIO;
- psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n",
- repeated_command, param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "%2.2X report: %3ph\n",
+ repeated_command, param);
return 0;
}
@@ -1274,9 +1274,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
psmouse_warn(psmouse, "trackstick E7 report failed\n");
ret = -ENODEV;
} else {
- psmouse_dbg(psmouse,
- "trackstick E7 report: %2.2x %2.2x %2.2x\n",
- param[0], param[1], param[2]);
+ psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
/*
* Not sure what this does, but it is absolutely
@@ -1323,6 +1321,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
if (reg_val == -EIO)
goto error;
+
if (reg_val == 0 &&
alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
goto error;
@@ -1676,8 +1675,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
}
psmouse_info(psmouse,
- "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n",
- e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]);
+ "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
return -EINVAL;
}
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
index 5fa99341a39..b55d5af217a 100644
--- a/drivers/input/mouse/amimouse.c
+++ b/drivers/input/mouse/amimouse.c
@@ -146,18 +146,6 @@ static struct platform_driver amimouse_driver = {
},
};
-static int __init amimouse_init(void)
-{
- return platform_driver_probe(&amimouse_driver, amimouse_probe);
-}
-
-module_init(amimouse_init);
-
-static void __exit amimouse_exit(void)
-{
- platform_driver_unregister(&amimouse_driver);
-}
-
-module_exit(amimouse_exit);
+module_platform_driver_probe(amimouse_driver, amimouse_probe);
MODULE_ALIAS("platform:amiga-mouse");
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index f3102494237..ca843b6cf6b 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -20,9 +20,34 @@
#include "trackpoint.h"
/*
+ * Power-on Reset: Resets all trackpoint parameters, including RAM values,
+ * to defaults.
+ * Returns zero on success, non-zero on failure.
+ */
+static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
+{
+ unsigned char results[2];
+ int tries = 0;
+
+ /* Issue POR command, and repeat up to once if 0xFC00 received */
+ do {
+ if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+ ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
+ return -1;
+ } while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
+
+ /* Check for success response -- 0xAA00 */
+ if (results[0] != 0xAA || results[1] != 0x00)
+ return -1;
+
+ return 0;
+}
+
+/*
* Device IO: read, write and toggle bit
*/
-static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results)
+static int trackpoint_read(struct ps2dev *ps2dev,
+ unsigned char loc, unsigned char *results)
{
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
@@ -32,7 +57,8 @@ static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned ch
return 0;
}
-static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val)
+static int trackpoint_write(struct ps2dev *ps2dev,
+ unsigned char loc, unsigned char val)
{
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
@@ -44,7 +70,8 @@ static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned c
return 0;
}
-static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask)
+static int trackpoint_toggle_bit(struct ps2dev *ps2dev,
+ unsigned char loc, unsigned char mask)
{
/* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F)
@@ -60,6 +87,18 @@ static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsig
return 0;
}
+static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc,
+ unsigned char mask, unsigned char value)
+{
+ int retval = 0;
+ unsigned char data;
+
+ trackpoint_read(ps2dev, loc, &data);
+ if (((data & mask) == mask) != !!value)
+ retval = trackpoint_toggle_bit(ps2dev, loc, mask);
+
+ return retval;
+}
/*
* Trackpoint-specific attributes
@@ -69,6 +108,7 @@ struct trackpoint_attr_data {
unsigned char command;
unsigned char mask;
unsigned char inverted;
+ unsigned char power_on_default;
};
static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)
@@ -102,10 +142,11 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
return count;
}
-#define TRACKPOINT_INT_ATTR(_name, _command) \
+#define TRACKPOINT_INT_ATTR(_name, _command, _default) \
static struct trackpoint_attr_data trackpoint_attr_##_name = { \
.field_offset = offsetof(struct trackpoint_data, _name), \
.command = _command, \
+ .power_on_default = _default, \
}; \
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
&trackpoint_attr_##_name, \
@@ -139,31 +180,60 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
}
-#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \
- static struct trackpoint_attr_data trackpoint_attr_##_name = { \
- .field_offset = offsetof(struct trackpoint_data, _name), \
- .command = _command, \
- .mask = _mask, \
- .inverted = _inv, \
- }; \
- PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
- &trackpoint_attr_##_name, \
- trackpoint_show_int_attr, trackpoint_set_bit_attr)
-
-TRACKPOINT_INT_ATTR(sensitivity, TP_SENS);
-TRACKPOINT_INT_ATTR(speed, TP_SPEED);
-TRACKPOINT_INT_ATTR(inertia, TP_INERTIA);
-TRACKPOINT_INT_ATTR(reach, TP_REACH);
-TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS);
-TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG);
-TRACKPOINT_INT_ATTR(thresh, TP_THRESH);
-TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH);
-TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME);
-TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV);
-
-TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0);
-TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0);
-TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1);
+#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \
+static struct trackpoint_attr_data trackpoint_attr_##_name = { \
+ .field_offset = offsetof(struct trackpoint_data, \
+ _name), \
+ .command = _command, \
+ .mask = _mask, \
+ .inverted = _inv, \
+ .power_on_default = _default, \
+ }; \
+PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ &trackpoint_attr_##_name, \
+ trackpoint_show_int_attr, trackpoint_set_bit_attr)
+
+#define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name) \
+do { \
+ struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \
+ \
+ trackpoint_update_bit(&_psmouse->ps2dev, \
+ _attr->command, _attr->mask, _tp->_name); \
+} while (0)
+
+#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \
+do { \
+ if (!_power_on || \
+ _tp->_name != trackpoint_attr_##_name.power_on_default) { \
+ if (!trackpoint_attr_##_name.mask) \
+ trackpoint_write(&_psmouse->ps2dev, \
+ trackpoint_attr_##_name.command, \
+ _tp->_name); \
+ else \
+ TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name); \
+ } \
+} while (0)
+
+#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \
+ (_tp->_name = trackpoint_attr_##_name.power_on_default)
+
+TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS);
+TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED);
+TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA);
+TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH);
+TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS);
+TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG);
+TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH);
+TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH);
+TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME);
+TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV);
+
+TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0,
+ TP_DEF_PTSON);
+TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0,
+ TP_DEF_SKIPBACK);
+TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1,
+ TP_DEF_EXT_DEV);
static struct attribute *trackpoint_attrs[] = {
&psmouse_attr_sensitivity.dattr.attr,
@@ -202,73 +272,72 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir
return 0;
}
-static int trackpoint_sync(struct psmouse *psmouse)
+/*
+ * Write parameters to trackpad.
+ * in_power_on_state: Set to true if TP is in default / power-on state (ex. if
+ * power-on reset was run). If so, values will only be
+ * written to TP if they differ from power-on default.
+ */
+static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state)
{
struct trackpoint_data *tp = psmouse->private;
- unsigned char toggle;
-
- /* Disable features that may make device unusable with this driver */
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle);
- if (toggle & TP_MASK_TWOHAND)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND);
-
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle);
- if (toggle & TP_MASK_SOURCE_TAG)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG);
-
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle);
- if (toggle & TP_MASK_MB)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB);
-
- /* Push the config to the device */
- trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity);
- trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia);
- trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed);
-
- trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach);
- trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys);
- trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag);
-
- trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh);
- trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh);
- trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime);
- trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks);
+ if (!in_power_on_state) {
+ /*
+ * Disable features that may make device unusable
+ * with this driver.
+ */
+ trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND,
+ TP_MASK_TWOHAND, TP_DEF_TWOHAND);
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle);
- if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);
+ trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG,
+ TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG);
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle);
- if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);
+ trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB,
+ TP_MASK_MB, TP_DEF_MB);
+ }
- trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle);
- if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev)
- trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);
+ /*
+ * These properties can be changed in this driver. Only
+ * configure them if the values are non-default or if the TP is in
+ * an unknown state.
+ */
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks);
+
+ /* toggles */
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback);
+ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev);
return 0;
}
static void trackpoint_defaults(struct trackpoint_data *tp)
{
- tp->press_to_select = TP_DEF_PTSON;
- tp->sensitivity = TP_DEF_SENS;
- tp->speed = TP_DEF_SPEED;
- tp->reach = TP_DEF_REACH;
-
- tp->draghys = TP_DEF_DRAGHYS;
- tp->mindrag = TP_DEF_MINDRAG;
-
- tp->thresh = TP_DEF_THRESH;
- tp->upthresh = TP_DEF_UP_THRESH;
-
- tp->ztime = TP_DEF_Z_TIME;
- tp->jenks = TP_DEF_JENKS_CURV;
-
- tp->inertia = TP_DEF_INERTIA;
- tp->skipback = TP_DEF_SKIPBACK;
- tp->ext_dev = TP_DEF_EXT_DEV;
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia);
+
+ /* toggles */
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback);
+ TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);
}
static void trackpoint_disconnect(struct psmouse *psmouse)
@@ -281,10 +350,13 @@ static void trackpoint_disconnect(struct psmouse *psmouse)
static int trackpoint_reconnect(struct psmouse *psmouse)
{
+ int reset_fail;
+
if (trackpoint_start_protocol(psmouse, NULL))
return -1;
- if (trackpoint_sync(psmouse))
+ reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev);
+ if (trackpoint_sync(psmouse, !reset_fail))
return -1;
return 0;
@@ -322,7 +394,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
trackpoint_defaults(psmouse->private);
- trackpoint_sync(psmouse);
+
+ error = trackpoint_power_on_reset(&psmouse->ps2dev);
+
+ /* Write defaults to TP only if reset fails. */
+ if (error)
+ trackpoint_sync(psmouse, false);
error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
if (error) {
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
index e558a709661..ecd0547964a 100644
--- a/drivers/input/mouse/trackpoint.h
+++ b/drivers/input/mouse/trackpoint.h
@@ -126,6 +126,8 @@
#define TP_DEF_PTSON 0x00
#define TP_DEF_SKIPBACK 0x00
#define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */
+#define TP_DEF_TWOHAND 0x00
+#define TP_DEF_SOURCE_TAG 0x00
#define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd))
@@ -136,9 +138,9 @@ struct trackpoint_data
unsigned char thresh, upthresh;
unsigned char ztime, jenks;
+ /* toggles */
unsigned char press_to_select;
unsigned char skipback;
-
unsigned char ext_dev;
};
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 3ec5ef2dd44..aebfe3ecb94 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -245,4 +245,14 @@ config SERIO_ARC_PS2
To compile this driver as a module, choose M here; the module
will be called arc_ps2.
+config SERIO_APBPS2
+ tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller"
+ depends on OF
+ help
+ Say Y here if you want support for GRLIB APBPS2 peripherals used
+ to connect to PS/2 keyboard and/or mouse.
+
+ To compile this driver as a module, choose M here: the module will
+ be called apbps2.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 4b0c8f84f1c..8edb36c2cdb 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o
obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
+obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c
new file mode 100644
index 00000000000..17e01a807dd
--- /dev/null
+++ b/drivers/input/serio/apbps2.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2013 Aeroflex Gaisler
+ *
+ * This driver supports the APBPS2 PS/2 core available in the GRLIB
+ * VHDL IP core library.
+ *
+ * Full documentation of the APBPS2 core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for
+ * information on open firmware properties.
+ *
+ * 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.
+ *
+ * Contributors: Daniel Hellstrom <daniel@gaisler.com>
+ */
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+struct apbps2_regs {
+ u32 __iomem data; /* 0x00 */
+ u32 __iomem status; /* 0x04 */
+ u32 __iomem ctrl; /* 0x08 */
+ u32 __iomem reload; /* 0x0c */
+};
+
+#define APBPS2_STATUS_DR (1<<0)
+#define APBPS2_STATUS_PE (1<<1)
+#define APBPS2_STATUS_FE (1<<2)
+#define APBPS2_STATUS_KI (1<<3)
+#define APBPS2_STATUS_RF (1<<4)
+#define APBPS2_STATUS_TF (1<<5)
+#define APBPS2_STATUS_TCNT (0x1f<<22)
+#define APBPS2_STATUS_RCNT (0x1f<<27)
+
+#define APBPS2_CTRL_RE (1<<0)
+#define APBPS2_CTRL_TE (1<<1)
+#define APBPS2_CTRL_RI (1<<2)
+#define APBPS2_CTRL_TI (1<<3)
+
+struct apbps2_priv {
+ struct serio *io;
+ struct apbps2_regs *regs;
+};
+
+static int apbps2_idx;
+
+static irqreturn_t apbps2_isr(int irq, void *dev_id)
+{
+ struct apbps2_priv *priv = dev_id;
+ unsigned long status, data, rxflags;
+ irqreturn_t ret = IRQ_NONE;
+
+ while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) {
+ data = ioread32be(&priv->regs->data);
+ rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0;
+ rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0;
+
+ /* clear error bits? */
+ if (rxflags)
+ iowrite32be(0, &priv->regs->status);
+
+ serio_interrupt(priv->io, data, rxflags);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int apbps2_write(struct serio *io, unsigned char val)
+{
+ struct apbps2_priv *priv = io->port_data;
+ unsigned int tleft = 10000; /* timeout in 100ms */
+
+ /* delay until PS/2 controller has room for more chars */
+ while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--)
+ udelay(10);
+
+ if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) {
+ iowrite32be(val, &priv->regs->data);
+
+ iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE,
+ &priv->regs->ctrl);
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int apbps2_open(struct serio *io)
+{
+ struct apbps2_priv *priv = io->port_data;
+ int limit;
+ unsigned long tmp;
+
+ /* clear error flags */
+ iowrite32be(0, &priv->regs->status);
+
+ /* Clear old data if available (unlikely) */
+ limit = 1024;
+ while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
+ tmp = ioread32be(&priv->regs->data);
+
+ /* Enable reciever and it's interrupt */
+ iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl);
+
+ return 0;
+}
+
+static void apbps2_close(struct serio *io)
+{
+ struct apbps2_priv *priv = io->port_data;
+
+ /* stop interrupts at PS/2 HW level */
+ iowrite32be(0, &priv->regs->ctrl);
+}
+
+/* Initialize one APBPS2 PS/2 core */
+static int apbps2_of_probe(struct platform_device *ofdev)
+{
+ struct apbps2_priv *priv;
+ int irq, err;
+ u32 freq_hz;
+ struct resource *res;
+
+ priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&ofdev->dev, "memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Find Device Address */
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_ioremap_resource(&ofdev->dev, res);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ /* Reset hardware, disable interrupt */
+ iowrite32be(0, &priv->regs->ctrl);
+
+ /* IRQ */
+ irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+ err = devm_request_irq(&ofdev->dev, irq, apbps2_isr,
+ IRQF_SHARED, "apbps2", priv);
+ if (err) {
+ dev_err(&ofdev->dev, "request IRQ%d failed\n", irq);
+ return err;
+ }
+
+ /* Get core frequency */
+ if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) {
+ dev_err(&ofdev->dev, "unable to get core frequency\n");
+ return -EINVAL;
+ }
+
+ /* Set reload register to core freq in kHz/10 */
+ iowrite32be(freq_hz / 10000, &priv->regs->reload);
+
+ priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!priv->io)
+ return -ENOMEM;
+
+ priv->io->id.type = SERIO_8042;
+ priv->io->open = apbps2_open;
+ priv->io->close = apbps2_close;
+ priv->io->write = apbps2_write;
+ priv->io->port_data = priv;
+ strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name));
+ snprintf(priv->io->phys, sizeof(priv->io->phys),
+ "apbps2_%d", apbps2_idx++);
+
+ dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
+
+ serio_register_port(priv->io);
+
+ platform_set_drvdata(ofdev, priv);
+
+ return 0;
+}
+
+static int apbps2_of_remove(struct platform_device *of_dev)
+{
+ struct apbps2_priv *priv = platform_get_drvdata(of_dev);
+
+ serio_unregister_port(priv->io);
+
+ return 0;
+}
+
+static struct of_device_id apbps2_of_match[] = {
+ { .name = "GAISLER_APBPS2", },
+ { .name = "01_060", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, apbps2_of_match);
+
+static struct platform_driver apbps2_of_driver = {
+ .driver = {
+ .name = "grlib-apbps2",
+ .owner = THIS_MODULE,
+ .of_match_table = apbps2_of_match,
+ },
+ .probe = apbps2_of_probe,
+ .remove = apbps2_of_remove,
+};
+
+module_platform_driver(apbps2_of_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c
index c52e3e589f7..3fb7727c8ea 100644
--- a/drivers/input/serio/arc_ps2.c
+++ b/drivers/input/serio/arc_ps2.c
@@ -14,6 +14,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -259,10 +260,19 @@ static int arc_ps2_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id arc_ps2_match[] = {
+ { .compatible = "snps,arc_ps2" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, arc_ps2_match);
+#endif
+
static struct platform_driver arc_ps2_driver = {
.driver = {
- .name = "arc_ps2",
- .owner = THIS_MODULE,
+ .name = "arc_ps2",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(arc_ps2_match),
},
.probe = arc_ps2_probe,
.remove = arc_ps2_remove,
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
index 36e799c31f5..190ce35af7d 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -359,18 +359,7 @@ static struct platform_driver psif_driver = {
},
};
-static int __init psif_init(void)
-{
- return platform_driver_probe(&psif_driver, psif_probe);
-}
-
-static void __exit psif_exit(void)
-{
- platform_driver_unregister(&psif_driver);
-}
-
-module_init(psif_init);
-module_exit(psif_exit);
+module_platform_driver_probe(psif_driver, psif_probe);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
index 70fe542839f..436a3433f8e 100644
--- a/drivers/input/serio/q40kbd.c
+++ b/drivers/input/serio/q40kbd.c
@@ -193,15 +193,4 @@ static struct platform_driver q40kbd_driver = {
.remove = q40kbd_remove,
};
-static int __init q40kbd_init(void)
-{
- return platform_driver_probe(&q40kbd_driver, q40kbd_probe);
-}
-
-static void __exit q40kbd_exit(void)
-{
- platform_driver_unregister(&q40kbd_driver);
-}
-
-module_init(q40kbd_init);
-module_exit(q40kbd_exit);
+module_platform_driver_probe(q40kbd_driver, q40kbd_probe);
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index 23fa829b869..f3a174a83c8 100644
--- a/drivers/input/touchscreen/ad7877.c
+++ b/drivers/input/touchscreen/ad7877.c
@@ -273,7 +273,7 @@ static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)
static int ad7877_read_adc(struct spi_device *spi, unsigned command)
{
- struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ad7877 *ts = spi_get_drvdata(spi);
struct ser_req *req;
int status;
int sample;
@@ -720,7 +720,7 @@ static int ad7877_probe(struct spi_device *spi)
goto err_free_mem;
}
- dev_set_drvdata(&spi->dev, ts);
+ spi_set_drvdata(spi, ts);
ts->spi = spi;
ts->input = input_dev;
@@ -806,13 +806,13 @@ err_free_irq:
err_free_mem:
input_free_device(input_dev);
kfree(ts);
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
return err;
}
static int ad7877_remove(struct spi_device *spi)
{
- struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ad7877 *ts = spi_get_drvdata(spi);
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
@@ -823,7 +823,7 @@ static int ad7877_remove(struct spi_device *spi)
kfree(ts);
dev_dbg(&spi->dev, "unregistered touchscreen\n");
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
return 0;
}
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 434c3df250c..84ccf140c1b 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -1245,7 +1245,7 @@ static int ads7846_probe(struct spi_device *spi)
goto err_free_mem;
}
- dev_set_drvdata(&spi->dev, ts);
+ spi_set_drvdata(spi, ts);
ts->packet = packet;
ts->spi = spi;
@@ -1397,7 +1397,7 @@ static int ads7846_probe(struct spi_device *spi)
static int ads7846_remove(struct spi_device *spi)
{
- struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+ struct ads7846 *ts = spi_get_drvdata(spi);
device_init_wakeup(&spi->dev, false);
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
index c5c2dbb9386..2c1e46b7e45 100644
--- a/drivers/input/touchscreen/atmel-wm97xx.c
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -432,17 +432,7 @@ static struct platform_driver atmel_wm97xx_driver = {
},
};
-static int __init atmel_wm97xx_init(void)
-{
- return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe);
-}
-module_init(atmel_wm97xx_init);
-
-static void __exit atmel_wm97xx_exit(void)
-{
- platform_driver_unregister(&atmel_wm97xx_driver);
-}
-module_exit(atmel_wm97xx_exit);
+module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
index c6e19a96348..d3f9f6b0f9b 100644
--- a/drivers/input/touchscreen/auo-pixcir-ts.c
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -31,6 +31,8 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/input/auo-pixcir-ts.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
/*
* Coordinate calculation:
@@ -111,6 +113,7 @@
struct auo_pixcir_ts {
struct i2c_client *client;
struct input_dev *input;
+ const struct auo_pixcir_ts_platdata *pdata;
char phys[32];
/* special handling for touch_indicate interupt mode */
@@ -132,7 +135,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
struct auo_point_t *point)
{
struct i2c_client *client = ts->client;
- const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
uint8_t raw_coord[8];
uint8_t raw_area[4];
int i, ret;
@@ -178,8 +181,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
{
struct auo_pixcir_ts *ts = dev_id;
- struct i2c_client *client = ts->client;
- const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
int i;
int ret;
@@ -290,7 +292,7 @@ static int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
int int_setting)
{
struct i2c_client *client = ts->client;
- struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
int ret;
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
@@ -479,53 +481,105 @@ unlock:
}
#endif
-static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend,
- auo_pixcir_resume);
+static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops,
+ auo_pixcir_suspend, auo_pixcir_resume);
+
+#ifdef CONFIG_OF
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+ struct auo_pixcir_ts_platdata *pdata;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return ERR_PTR(-ENOENT);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->gpio_int = of_get_gpio(np, 0);
+ if (!gpio_is_valid(pdata->gpio_int)) {
+ dev_err(dev, "failed to get interrupt gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata->gpio_rst = of_get_gpio(np, 1);
+ if (!gpio_is_valid(pdata->gpio_rst)) {
+ dev_err(dev, "failed to get reset gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+ dev_err(dev, "failed to get x-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+ dev_err(dev, "failed to get y-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* default to asserting the interrupt when the screen is touched */
+ pdata->int_setting = AUO_PIXCIR_INT_TOUCH_IND;
+
+ return pdata;
+}
+#else
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+static void auo_pixcir_reset(void *data)
+{
+ struct auo_pixcir_ts *ts = data;
+
+ gpio_set_value(ts->pdata->gpio_rst, 0);
+}
static int auo_pixcir_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ const struct auo_pixcir_ts_platdata *pdata;
struct auo_pixcir_ts *ts;
struct input_dev *input_dev;
- int ret;
-
- if (!pdata)
- return -EINVAL;
+ int version;
+ int error;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = auo_pixcir_parse_dt(&client->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
- ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL);
+ ts = devm_kzalloc(&client->dev,
+ sizeof(struct auo_pixcir_ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
- ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int");
- if (ret) {
- dev_err(&client->dev, "request of gpio %d failed, %d\n",
- pdata->gpio_int, ret);
- goto err_gpio_int;
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "could not allocate input device\n");
+ return -ENOMEM;
}
- if (pdata->init_hw)
- pdata->init_hw(client);
-
+ ts->pdata = pdata;
ts->client = client;
+ ts->input = input_dev;
ts->touch_ind_mode = 0;
+ ts->stopped = true;
init_waitqueue_head(&ts->wait);
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
- input_dev = input_allocate_device();
- if (!input_dev) {
- dev_err(&client->dev, "could not allocate input device\n");
- goto err_input_alloc;
- }
-
- ts->input = input_dev;
-
input_dev->name = "AUO-Pixcir touchscreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
input_dev->open = auo_pixcir_input_open;
input_dev->close = auo_pixcir_input_close;
@@ -550,70 +604,70 @@ static int auo_pixcir_probe(struct i2c_client *client,
AUO_PIXCIR_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
- ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
- if (ret < 0)
- goto err_fw_vers;
- dev_info(&client->dev, "firmware version 0x%X\n", ret);
-
- ret = auo_pixcir_int_config(ts, pdata->int_setting);
- if (ret)
- goto err_fw_vers;
-
input_set_drvdata(ts->input, ts);
- ts->stopped = true;
- ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- input_dev->name, ts);
- if (ret) {
- dev_err(&client->dev, "irq %d requested failed\n", client->irq);
- goto err_fw_vers;
+ error = devm_gpio_request_one(&client->dev, pdata->gpio_int,
+ GPIOF_DIR_IN, "auo_pixcir_ts_int");
+ if (error) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_int, error);
+ return error;
}
- /* stop device and put it into deep sleep until it is opened */
- ret = auo_pixcir_stop(ts);
- if (ret < 0)
- goto err_input_register;
-
- ret = input_register_device(input_dev);
- if (ret) {
- dev_err(&client->dev, "could not register input device\n");
- goto err_input_register;
+ error = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+ "auo_pixcir_ts_rst");
+ if (error) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_rst, error);
+ return error;
}
- i2c_set_clientdata(client, ts);
-
- return 0;
+ error = devm_add_action(&client->dev, auo_pixcir_reset, ts);
+ if (error) {
+ auo_pixcir_reset(ts);
+ dev_err(&client->dev, "failed to register reset action, %d\n",
+ error);
+ return error;
+ }
-err_input_register:
- free_irq(client->irq, ts);
-err_fw_vers:
- input_free_device(input_dev);
-err_input_alloc:
- if (pdata->exit_hw)
- pdata->exit_hw(client);
- gpio_free(pdata->gpio_int);
-err_gpio_int:
- kfree(ts);
+ msleep(200);
- return ret;
-}
-
-static int auo_pixcir_remove(struct i2c_client *client)
-{
- struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
- const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
+ if (version < 0) {
+ error = version;
+ return error;
+ }
- free_irq(client->irq, ts);
+ dev_info(&client->dev, "firmware version 0x%X\n", version);
- input_unregister_device(ts->input);
+ error = auo_pixcir_int_config(ts, pdata->int_setting);
+ if (error)
+ return error;
- if (pdata->exit_hw)
- pdata->exit_hw(client);
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, auo_pixcir_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ input_dev->name, ts);
+ if (error) {
+ dev_err(&client->dev, "irq %d requested failed, %d\n",
+ client->irq, error);
+ return error;
+ }
- gpio_free(pdata->gpio_int);
+ /* stop device and put it into deep sleep until it is opened */
+ error = auo_pixcir_stop(ts);
+ if (error)
+ return error;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&client->dev, "could not register input device, %d\n",
+ error);
+ return error;
+ }
- kfree(ts);
+ i2c_set_clientdata(client, ts);
return 0;
}
@@ -624,14 +678,22 @@ static const struct i2c_device_id auo_pixcir_idtable[] = {
};
MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
+#ifdef CONFIG_OF
+static struct of_device_id auo_pixcir_ts_dt_idtable[] = {
+ { .compatible = "auo,auo_pixcir_ts" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable);
+#endif
+
static struct i2c_driver auo_pixcir_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "auo_pixcir_ts",
.pm = &auo_pixcir_pm_ops,
+ .of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable),
},
.probe = auo_pixcir_probe,
- .remove = auo_pixcir_remove,
.id_table = auo_pixcir_idtable,
};
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index a9170157b44..83fa1b15a97 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -440,8 +440,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
return -EIO;
}
- if (tsdata->raw_buffer)
- kfree(tsdata->raw_buffer);
+ kfree(tsdata->raw_buffer);
tsdata->raw_buffer = NULL;
/* restore parameters */
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 55255a94007..8fe5086c8d2 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -206,8 +206,7 @@ static int eeti_ts_probe(struct i2c_client *client,
if (err < 0)
goto err1;
- if (pdata)
- priv->irq_active_high = pdata->irq_active_high;
+ priv->irq_active_high = pdata->irq_active_high;
irq_flags = priv->irq_active_high ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index 02103b6abb3..89308fe3875 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -250,17 +250,7 @@ static struct platform_driver mc13783_ts_driver = {
},
};
-static int __init mc13783_ts_init(void)
-{
- return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe);
-}
-module_init(mc13783_ts_init);
-
-static void __exit mc13783_ts_exit(void)
-{
- platform_driver_unregister(&mc13783_ts_driver);
-}
-module_exit(mc13783_ts_exit);
+module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe);
MODULE_DESCRIPTION("MC13783 input touchscreen driver");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index d9d05e22242..1740a249637 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -19,13 +19,16 @@
*/
#include <linux/delay.h>
+#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/platform_data/st1232_pdata.h>
#define ST1232_TS_NAME "st1232-ts"
@@ -48,6 +51,7 @@ struct st1232_ts_data {
struct input_dev *input_dev;
struct st1232_ts_finger finger[MAX_FINGERS];
struct dev_pm_qos_request low_latency_req;
+ int reset_gpio;
};
static int st1232_ts_read_data(struct st1232_ts_data *ts)
@@ -139,10 +143,17 @@ end:
return IRQ_HANDLED;
}
+static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
+{
+ if (gpio_is_valid(ts->reset_gpio))
+ gpio_direction_output(ts->reset_gpio, poweron);
+}
+
static int st1232_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct st1232_ts_data *ts;
+ struct st1232_pdata *pdata = client->dev.platform_data;
struct input_dev *input_dev;
int error;
@@ -156,17 +167,36 @@ static int st1232_ts_probe(struct i2c_client *client,
return -EINVAL;
}
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
- ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ts || !input_dev) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev)
+ return -ENOMEM;
ts->client = client;
ts->input_dev = input_dev;
+ if (pdata)
+ ts->reset_gpio = pdata->reset_gpio;
+ else if (client->dev.of_node)
+ ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
+ else
+ ts->reset_gpio = -ENODEV;
+
+ if (gpio_is_valid(ts->reset_gpio)) {
+ error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
+ if (error) {
+ dev_err(&client->dev,
+ "Unable to request GPIO pin %d.\n",
+ ts->reset_gpio);
+ return error;
+ }
+ }
+
+ st1232_ts_power(ts, true);
+
input_dev->name = "st1232-touchscreen";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
@@ -179,31 +209,26 @@ static int st1232_ts_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
- error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler,
- IRQF_ONESHOT, client->name, ts);
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, st1232_ts_irq_handler,
+ IRQF_ONESHOT,
+ client->name, ts);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_free_mem;
+ return error;
}
error = input_register_device(ts->input_dev);
if (error) {
dev_err(&client->dev, "Unable to register %s input device\n",
input_dev->name);
- goto err_free_irq;
+ return error;
}
i2c_set_clientdata(client, ts);
device_init_wakeup(&client->dev, 1);
return 0;
-
-err_free_irq:
- free_irq(client->irq, ts);
-err_free_mem:
- input_free_device(input_dev);
- kfree(ts);
- return error;
}
static int st1232_ts_remove(struct i2c_client *client)
@@ -211,9 +236,7 @@ static int st1232_ts_remove(struct i2c_client *client)
struct st1232_ts_data *ts = i2c_get_clientdata(client);
device_init_wakeup(&client->dev, 0);
- free_irq(client->irq, ts);
- input_unregister_device(ts->input_dev);
- kfree(ts);
+ st1232_ts_power(ts, false);
return 0;
}
@@ -222,11 +245,14 @@ static int st1232_ts_remove(struct i2c_client *client)
static int st1232_ts_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
- if (device_may_wakeup(&client->dev))
+ if (device_may_wakeup(&client->dev)) {
enable_irq_wake(client->irq);
- else
+ } else {
disable_irq(client->irq);
+ st1232_ts_power(ts, false);
+ }
return 0;
}
@@ -234,11 +260,14 @@ static int st1232_ts_suspend(struct device *dev)
static int st1232_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
- if (device_may_wakeup(&client->dev))
+ if (device_may_wakeup(&client->dev)) {
disable_irq_wake(client->irq);
- else
+ } else {
+ st1232_ts_power(ts, true);
enable_irq(client->irq);
+ }
return 0;
}
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
index 6e743e3dfda..16b52115c27 100644
--- a/drivers/input/touchscreen/wm9712.c
+++ b/drivers/input/touchscreen/wm9712.c
@@ -162,14 +162,14 @@ static void wm9712_phy_init(struct wm97xx *wm)
if (rpu) {
dig2 &= 0xffc0;
dig2 |= WM9712_RPU(rpu);
- dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms",
+ dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n",
64000 / rpu);
}
/* WM9712 five wire */
if (five_wire) {
dig2 |= WM9712_45W;
- dev_dbg(wm->dev, "setting 5-wire touchscreen mode.");
+ dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n");
if (pil) {
dev_warn(wm->dev, "pressure measurement is not "
@@ -182,21 +182,21 @@ static void wm9712_phy_init(struct wm97xx *wm)
if (pil == 2) {
dig2 |= WM9712_PIL;
dev_dbg(wm->dev,
- "setting pressure measurement current to 400uA.");
+ "setting pressure measurement current to 400uA.\n");
} else if (pil)
dev_dbg(wm->dev,
- "setting pressure measurement current to 200uA.");
+ "setting pressure measurement current to 200uA.\n");
if (!pil)
pressure = 0;
/* polling mode sample settling delay */
if (delay < 0 || delay > 15) {
- dev_dbg(wm->dev, "supplied delay out of range.");
+ dev_dbg(wm->dev, "supplied delay out of range.\n");
delay = 4;
}
dig1 &= 0xff0f;
dig1 |= WM97XX_DELAY(delay);
- dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n",
delay_table[delay]);
/* mask */
@@ -285,7 +285,7 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
if (is_pden(wm))
wm->pen_probably_down = 0;
else
- dev_dbg(wm->dev, "adc sample timeout");
+ dev_dbg(wm->dev, "adc sample timeout\n");
return RC_PENUP;
}
@@ -295,15 +295,19 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
/* check we have correct sample */
if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
- dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n",
adcsel & WM97XX_ADCSEL_MASK,
*sample & WM97XX_ADCSEL_MASK);
- return RC_PENUP;
+ return RC_AGAIN;
}
if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
- wm->pen_probably_down = 0;
- return RC_PENUP;
+ /* Sometimes it reads a wrong value the first time. */
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
}
return RC_VALID;
@@ -345,7 +349,7 @@ static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
if (is_pden(wm))
wm->pen_probably_down = 0;
else
- dev_dbg(wm->dev, "adc sample timeout");
+ dev_dbg(wm->dev, "adc sample timeout\n");
return RC_PENUP;
}
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
index 5dbe73af2f8..7e45c9f6e6b 100644
--- a/drivers/input/touchscreen/wm97xx-core.c
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -442,6 +442,16 @@ static int wm97xx_read_samples(struct wm97xx *wm)
"pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
data.x >> 12, data.x & 0xfff, data.y >> 12,
data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+
+ if (abs_x[0] > (data.x & 0xfff) ||
+ abs_x[1] < (data.x & 0xfff) ||
+ abs_y[0] > (data.y & 0xfff) ||
+ abs_y[1] < (data.y & 0xfff)) {
+ dev_dbg(wm->dev, "Measurement out of range, dropping it\n");
+ rc = RC_AGAIN;
+ goto out;
+ }
+
input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
@@ -455,6 +465,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
wm->ts_reader_interval = wm->ts_reader_min_interval;
}
+out:
mutex_unlock(&wm->codec_mutex);
return rc;
}
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 0a0de333c76..b51c15408ff 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -43,6 +43,7 @@
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/moduleparam.h>
+#include <linux/jiffies.h>
#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -51,6 +52,9 @@
static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
static bool __read_mostly sysrq_always_enabled;
+unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
+int sysrq_reset_downtime_ms __weak;
+
static bool sysrq_on(void)
{
return sysrq_enabled || sysrq_always_enabled;
@@ -586,6 +590,7 @@ struct sysrq_state {
int reset_seq_len;
int reset_seq_cnt;
int reset_seq_version;
+ struct timer_list keyreset_timer;
};
#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
state->reset_seq_version = sysrq_reset_seq_version;
}
-static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
+static void sysrq_do_reset(unsigned long dummy)
+{
+ __handle_sysrq(sysrq_xlate[KEY_B], false);
+}
+
+static void sysrq_handle_reset_request(struct sysrq_state *state)
+{
+ if (sysrq_reset_downtime_ms)
+ mod_timer(&state->keyreset_timer,
+ jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms));
+ else
+ sysrq_do_reset(0);
+}
+
+static void sysrq_detect_reset_sequence(struct sysrq_state *state,
unsigned int code, int value)
{
if (!test_bit(code, state->reset_keybit)) {
/*
* Pressing any key _not_ in reset sequence cancels
- * the reset sequence.
+ * the reset sequence. Also cancelling the timer in
+ * case additional keys were pressed after a reset
+ * has been requested.
*/
- if (value && state->reset_seq_cnt)
+ if (value && state->reset_seq_cnt) {
state->reset_canceled = true;
+ del_timer(&state->keyreset_timer);
+ }
} else if (value == 0) {
- /* key release */
+ /*
+ * Key release - all keys in the reset sequence need
+ * to be pressed and held for the reset timeout
+ * to hold.
+ */
+ del_timer(&state->keyreset_timer);
+
if (--state->reset_seq_cnt == 0)
state->reset_canceled = false;
} else if (value == 1) {
/* key press, not autorepeat */
if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) {
- return true;
+ sysrq_handle_reset_request(state);
}
}
-
- return false;
}
static void sysrq_reinject_alt_sysrq(struct work_struct *work)
@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
if (was_active)
schedule_work(&sysrq->reinject_work);
- if (sysrq_detect_reset_sequence(sysrq, code, value)) {
- /* Force emergency reboot */
- __handle_sysrq(sysrq_xlate[KEY_B], false);
- }
+ /* Check for reset sequence */
+ sysrq_detect_reset_sequence(sysrq, code, value);
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
/*
@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler,
sysrq->handle.handler = handler;
sysrq->handle.name = "sysrq";
sysrq->handle.private = sysrq;
+ setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0);
error = input_register_handle(&sysrq->handle);
if (error) {
@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle)
input_close_device(handle);
cancel_work_sync(&sysrq->reinject_work);
+ del_timer_sync(&sysrq->keyreset_timer);
input_unregister_handle(handle);
kfree(sysrq);
}
@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = {
static bool sysrq_handler_registered;
-unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
-
static inline void sysrq_register_handler(void)
{
unsigned short key;
@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = {
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
&sysrq_reset_seq_len, 0644);
+module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);
+
#else
static inline void sysrq_register_handler(void)
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 171d7a9df3a..9b1cbcf8fb7 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -962,6 +962,10 @@ static int acm_probe(struct usb_interface *intf,
/* normal quirks */
quirks = (unsigned long)id->driver_info;
+
+ if (quirks == IGNORE_DEVICE)
+ return -ENODEV;
+
num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
/* handle quirks deadly to normal probing*/
@@ -1675,6 +1679,15 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = NO_DATA_INTERFACE,
},
+#if IS_ENABLED(CONFIG_INPUT_IMS_PCU)
+ { USB_DEVICE(0x04d8, 0x0082), /* Application mode */
+ .driver_info = IGNORE_DEVICE,
+ },
+ { USB_DEVICE(0x04d8, 0x0083), /* Bootloader mode */
+ .driver_info = IGNORE_DEVICE,
+ },
+#endif
+
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 35ef887b741..0f76e4af600 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -128,3 +128,4 @@ struct acm {
#define NO_CAP_LINE 4
#define NOT_A_MODEM 8
#define NO_DATA_INTERFACE 16
+#define IGNORE_DEVICE 32
diff --git a/include/linux/device.h b/include/linux/device.h
index 711793b145f..c0a12612532 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -576,6 +576,10 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
void __iomem *devm_request_and_ioremap(struct device *dev,
struct resource *res);
+/* allows to add/remove a custom action to devres stack */
+int devm_add_action(struct device *dev, void (*action)(void *), void *data);
+void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
+
struct device_dma_parameters {
/*
* a low level driver may set these to teach IOMMU code about
diff --git a/include/linux/input/auo-pixcir-ts.h b/include/linux/input/auo-pixcir-ts.h
index 75d4be71771..5049f21928e 100644
--- a/include/linux/input/auo-pixcir-ts.h
+++ b/include/linux/input/auo-pixcir-ts.h
@@ -43,12 +43,10 @@
*/
struct auo_pixcir_ts_platdata {
int gpio_int;
+ int gpio_rst;
int int_setting;
- void (*init_hw)(struct i2c_client *);
- void (*exit_hw)(struct i2c_client *);
-
unsigned int x_max;
unsigned int y_max;
};
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
index 2e86bd0bfba..1b1dfa80d9f 100644
--- a/include/linux/input/mt.h
+++ b/include/linux/input/mt.h
@@ -19,6 +19,7 @@
#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
+#define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */
/**
* struct input_mt_slot - represents the state of an input MT slot
diff --git a/include/linux/platform_data/st1232_pdata.h b/include/linux/platform_data/st1232_pdata.h
new file mode 100644
index 00000000000..cac3e7b4c45
--- /dev/null
+++ b/include/linux/platform_data/st1232_pdata.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_ST1232_PDATA_H
+#define _LINUX_ST1232_PDATA_H
+
+/*
+ * Optional platform data
+ *
+ * Use this if you want the driver to drive the reset pin.
+ */
+struct st1232_pdata {
+ int reset_gpio;
+};
+
+#endif
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 935119c698a..4649ee35b60 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -702,6 +702,11 @@ struct input_keymap_entry {
#define KEY_CAMERA_LEFT 0x219
#define KEY_CAMERA_RIGHT 0x21a
+#define KEY_ATTENDANT_ON 0x21b
+#define KEY_ATTENDANT_OFF 0x21c
+#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */
+#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */
+
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1