aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2013-05-01 08:47:44 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-05-01 08:47:44 -0700
commitbf61c8840efe60fd8f91446860b63338fb424158 (patch)
tree7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/input
parentInput: wacom - fix "can not retrieve extra class descriptor" for DTH2242 (diff)
parentInput: trackpoint - Optimize trackpoint init to use power-on reset (diff)
downloadlinux-dev-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz
linux-dev-bf61c8840efe60fd8f91446860b63338fb424158.zip
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/input-mt.c10
-rw-r--r--drivers/input/joystick/analog.c2
-rw-r--r--drivers/input/keyboard/Kconfig3
-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/matrix_keypad.c8
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c12
-rw-r--r--drivers/input/keyboard/spear-keyboard.c8
-rw-r--r--drivers/input/keyboard/tca8418_keypad.c2
-rw-r--r--drivers/input/keyboard/tegra-kbc.c91
-rw-r--r--drivers/input/misc/Kconfig20
-rw-r--r--drivers/input/misc/Makefile2
-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/arizona-haptics.c255
-rw-r--r--drivers/input/misc/atlas_btns.c2
-rw-r--r--drivers/input/misc/da9052_onkey.c22
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c15
-rw-r--r--drivers/input/misc/ims-pcu.c1901
-rw-r--r--drivers/input/misc/max8925_onkey.c3
-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/Kconfig13
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/apbps2.c228
-rw-r--r--drivers/input/serio/arc_ps2.c21
-rw-r--r--drivers/input/serio/at32psif.c13
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h9
-rw-r--r--drivers/input/serio/q40kbd.c13
-rw-r--r--drivers/input/touchscreen/Kconfig6
-rw-r--r--drivers/input/touchscreen/Makefile2
-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/atmel_mxt_ts.c68
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c2
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c226
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c59
-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/ti_am335x_tsc.c398
-rw-r--r--drivers/input/touchscreen/ti_tscadc.c486
-rw-r--r--drivers/input/touchscreen/wm9712.c28
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c11
53 files changed, 3472 insertions, 1003 deletions
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 53eaf89f2db5..d398f1321f14 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,20 +248,24 @@ 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;
if (mt->flags & INPUT_MT_DROP_UNUSED) {
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
- if (s->frame == mt->frame)
+ if (input_mt_is_used(mt, s))
continue;
input_mt_slot(dev, s - mt->slots);
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
}
}
- 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/joystick/analog.c b/drivers/input/joystick/analog.c
index 358cd7ee905b..7cd74e29cbc8 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -162,7 +162,7 @@ static unsigned int get_time_pit(void)
#define GET_TIME(x) do { x = get_cycles(); } while (0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "PCC"
-#elif defined(CONFIG_MN10300)
+#elif defined(CONFIG_MN10300) || defined(CONFIG_TILE)
#define GET_TIME(x) do { x = get_cycles(); } while (0)
#define DELTA(x, y) ((x) - (y))
#define TIME_NAME "TSC"
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 992137cf3a64..ac0500667000 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -409,7 +409,7 @@ config KEYBOARD_NEWTON
config KEYBOARD_NOMADIK
tristate "ST-Ericsson Nomadik SKE keyboard"
- depends on PLAT_NOMADIK
+ depends on (ARCH_NOMADIK || ARCH_U8500)
select INPUT_MATRIXKMAP
help
Say Y here if you want to use a keypad provided on the SKE controller
@@ -554,6 +554,7 @@ config KEYBOARD_OMAP
config KEYBOARD_OMAP4
tristate "TI OMAP4+ keypad support"
+ depends on ARCH_OMAP2PLUS
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the OMAP4+ keypad.
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
index 79172af164f2..ba0b36f7daea 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 4e4e453ea15e..829753702b62 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 98f9113251d2..03c8cc5cb6c1 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/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index f4ff0dda7597..71d77192ac1e 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -403,7 +403,7 @@ matrix_keypad_parse_dt(struct device *dev)
struct matrix_keypad_platform_data *pdata;
struct device_node *np = dev->of_node;
unsigned int *gpios;
- int i;
+ int i, nrow, ncol;
if (!np) {
dev_err(dev, "device lacks DT data\n");
@@ -416,9 +416,9 @@ matrix_keypad_parse_dt(struct device *dev)
return ERR_PTR(-ENOMEM);
}
- pdata->num_row_gpios = of_gpio_named_count(np, "row-gpios");
- pdata->num_col_gpios = of_gpio_named_count(np, "col-gpios");
- if (!pdata->num_row_gpios || !pdata->num_col_gpios) {
+ pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
+ pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
+ if (nrow <= 0 || ncol <= 0) {
dev_err(dev, "number of keypad rows/columns not specified\n");
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index 0e6a8151fee3..c7d505cce72f 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/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
index 695d237417d6..cb1e8f614631 100644
--- a/drivers/input/keyboard/spear-keyboard.c
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -228,11 +228,9 @@ static int spear_kbd_probe(struct platform_device *pdev)
kbd->suspended_rate = pdata->suspended_rate;
}
- kbd->io_base = devm_request_and_ioremap(&pdev->dev, res);
- if (!kbd->io_base) {
- dev_err(&pdev->dev, "request-ioremap failed for kbd_region\n");
- return -ENOMEM;
- }
+ kbd->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(kbd->io_base))
+ return PTR_ERR(kbd->io_base);
kbd->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbd->clk))
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
index 50e9c5e195e1..a34cc6714e5b 100644
--- a/drivers/input/keyboard/tca8418_keypad.c
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -384,7 +384,7 @@ static const struct i2c_device_id tca8418_id[] = {
MODULE_DEVICE_TABLE(i2c, tca8418_id);
#ifdef CONFIG_OF
-static const struct of_device_id tca8418_dt_ids[] __devinitconst = {
+static const struct of_device_id tca8418_dt_ids[] = {
{ .compatible = "ti,tca8418", },
{ }
};
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index d89e7d392d1e..b46142f78ef2 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 <mach/clk.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 2a1647ef5610..af80928a46b4 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -72,6 +72,16 @@ config INPUT_AD714X_SPI
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
+config INPUT_ARIZONA_HAPTICS
+ tristate "Arizona haptics support"
+ depends on MFD_ARIZONA && SND_SOC
+ select INPUT_FF_MEMLESS
+ help
+ Say Y to enable support for the haptics module in Arizona CODECs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called arizona-haptics.
+
config INPUT_BMA150
tristate "BMA150/SMB380 acceleration sensor support"
depends on I2C
@@ -580,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 1f874afeea6a..d7fc17f11d77 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
@@ -28,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 29d2064c26f2..e0f522516ef5 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 bdccca42d138..61891486067c 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 535dda48cace..416f47ddcc90 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 ad5f40d37e48..76dc0679d3b1 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/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
new file mode 100644
index 000000000000..7a04f54ef961
--- /dev/null
+++ b/drivers/input/misc/arizona-haptics.c
@@ -0,0 +1,255 @@
+/*
+ * Arizona haptics driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_haptics {
+ struct arizona *arizona;
+ struct input_dev *input_dev;
+ struct work_struct work;
+
+ struct mutex mutex;
+ u8 intensity;
+};
+
+static void arizona_haptics_work(struct work_struct *work)
+{
+ struct arizona_haptics *haptics = container_of(work,
+ struct arizona_haptics,
+ work);
+ struct arizona *arizona = haptics->arizona;
+ struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex;
+ int ret;
+
+ if (!haptics->arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return;
+ }
+
+ if (haptics->intensity) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_PHASE_2_INTENSITY,
+ ARIZONA_PHASE2_INTENSITY_MASK,
+ haptics->intensity);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set intensity: %d\n",
+ ret);
+ return;
+ }
+
+ /* This enable sequence will be a noop if already enabled */
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start haptics: %d\n",
+ ret);
+ return;
+ }
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ } else {
+ /* This disable sequence will be a noop if already enabled */
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to stop haptics: %d\n",
+ ret);
+ return;
+ }
+ }
+}
+
+static int arizona_haptics_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct arizona *arizona = haptics->arizona;
+
+ if (!arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return -EBUSY;
+ }
+
+ if (effect->u.rumble.strong_magnitude) {
+ /* Scale the magnitude into the range the device supports */
+ if (arizona->pdata.hap_act) {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 9;
+ if (effect->direction < 0x8000)
+ haptics->intensity += 0x7f;
+ } else {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 8;
+ }
+ } else {
+ haptics->intensity = 0;
+ }
+
+ schedule_work(&haptics->work);
+
+ return 0;
+}
+
+static void arizona_haptics_close(struct input_dev *input)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex;
+
+ cancel_work_sync(&haptics->work);
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ if (haptics->arizona->dapm)
+ snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
+
+ mutex_unlock(dapm_mutex);
+}
+
+static int arizona_haptics_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_haptics *haptics;
+ int ret;
+
+ haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
+ if (!haptics)
+ return -ENOMEM;
+
+ haptics->arizona = arizona;
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_ACT, arizona->pdata.hap_act);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
+ ret);
+ return ret;
+ }
+
+ INIT_WORK(&haptics->work, arizona_haptics_work);
+
+ haptics->input_dev = input_allocate_device();
+ if (haptics->input_dev == NULL) {
+ dev_err(arizona->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(haptics->input_dev, haptics);
+
+ haptics->input_dev->name = "arizona:haptics";
+ haptics->input_dev->dev.parent = pdev->dev.parent;
+ haptics->input_dev->close = arizona_haptics_close;
+ __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
+
+ ret = input_ff_create_memless(haptics->input_dev, NULL,
+ arizona_haptics_play);
+ if (ret < 0) {
+ dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+ ret);
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(haptics->input_dev);
+ if (ret < 0) {
+ dev_err(arizona->dev, "couldn't register input device: %d\n",
+ ret);
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, haptics);
+
+ return 0;
+
+err_iff:
+ if (haptics->input_dev)
+ input_ff_destroy(haptics->input_dev);
+err_ialloc:
+ input_free_device(haptics->input_dev);
+
+ return ret;
+}
+
+static int arizona_haptics_remove(struct platform_device *pdev)
+{
+ struct arizona_haptics *haptics = platform_get_drvdata(pdev);
+
+ input_unregister_device(haptics->input_dev);
+
+ return 0;
+}
+
+static struct platform_driver arizona_haptics_driver = {
+ .probe = arizona_haptics_probe,
+ .remove = arizona_haptics_remove,
+ .driver = {
+ .name = "arizona-haptics",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(arizona_haptics_driver);
+
+MODULE_ALIAS("platform:arizona-haptics");
+MODULE_DESCRIPTION("Arizona haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
index 26f13131639a..5d4402365a52 100644
--- a/drivers/input/misc/atlas_btns.c
+++ b/drivers/input/misc/atlas_btns.c
@@ -121,7 +121,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
return err;
}
-static int atlas_acpi_button_remove(struct acpi_device *device, int type)
+static int atlas_acpi_button_remove(struct acpi_device *device)
{
acpi_status status;
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
index 630c1ce4980a..020569a499f2 100644
--- a/drivers/input/misc/da9052_onkey.c
+++ b/drivers/input/misc/da9052_onkey.c
@@ -24,7 +24,6 @@ struct da9052_onkey {
struct da9052 *da9052;
struct input_dev *input;
struct delayed_work work;
- unsigned int irq;
};
static void da9052_onkey_query(struct da9052_onkey *onkey)
@@ -76,7 +75,6 @@ static int da9052_onkey_probe(struct platform_device *pdev)
struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
struct da9052_onkey *onkey;
struct input_dev *input_dev;
- int irq;
int error;
if (!da9052) {
@@ -84,13 +82,6 @@ static int da9052_onkey_probe(struct platform_device *pdev)
return -EINVAL;
}
- irq = platform_get_irq_byname(pdev, "ONKEY");
- if (irq < 0) {
- dev_err(&pdev->dev,
- "Failed to get an IRQ for input device, %d\n", irq);
- return -EINVAL;
- }
-
onkey = kzalloc(sizeof(*onkey), GFP_KERNEL);
input_dev = input_allocate_device();
if (!onkey || !input_dev) {
@@ -101,7 +92,6 @@ static int da9052_onkey_probe(struct platform_device *pdev)
onkey->input = input_dev;
onkey->da9052 = da9052;
- onkey->irq = irq;
INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work);
input_dev->name = "da9052-onkey";
@@ -111,13 +101,11 @@ static int da9052_onkey_probe(struct platform_device *pdev)
input_dev->evbit[0] = BIT_MASK(EV_KEY);
__set_bit(KEY_POWER, input_dev->keybit);
- error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "ONKEY", onkey);
+ error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY",
+ da9052_onkey_irq, onkey);
if (error < 0) {
dev_err(onkey->da9052->dev,
- "Failed to register ONKEY IRQ %d, error = %d\n",
- onkey->irq, error);
+ "Failed to register ONKEY IRQ: %d\n", error);
goto err_free_mem;
}
@@ -132,7 +120,7 @@ static int da9052_onkey_probe(struct platform_device *pdev)
return 0;
err_free_irq:
- free_irq(onkey->irq, onkey);
+ da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
cancel_delayed_work_sync(&onkey->work);
err_free_mem:
input_free_device(input_dev);
@@ -145,7 +133,7 @@ static int da9052_onkey_remove(struct platform_device *pdev)
{
struct da9052_onkey *onkey = platform_get_drvdata(pdev);
- free_irq(onkey->irq, onkey);
+ da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
cancel_delayed_work_sync(&onkey->work);
input_unregister_device(onkey->input);
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index 0b4f54265f62..2e3334b8f82d 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -109,7 +109,9 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
if (hp_sdc_enqueue_transaction(&t)) return -1;
- down_interruptible(&tsem); /* Put ourselves to sleep for results. */
+ /* Put ourselves to sleep for results. */
+ if (WARN_ON(down_interruptible(&tsem)))
+ return -1;
/* Check for nonpresence of BBRTC */
if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
@@ -176,11 +178,16 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
t.seq = tseq;
t.act.semaphore = &i8042tregs;
- down_interruptible(&i8042tregs); /* Sleep if output regs in use. */
+ /* Sleep if output regs in use. */
+ if (WARN_ON(down_interruptible(&i8042tregs)))
+ return -1;
if (hp_sdc_enqueue_transaction(&t)) return -1;
- down_interruptible(&i8042tregs); /* Sleep until results come back. */
+ /* Sleep until results come back. */
+ if (WARN_ON(down_interruptible(&i8042tregs)))
+ return -1;
+
up(&i8042tregs);
return (tseq[5] |
@@ -276,6 +283,7 @@ static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
}
+#if 0 /* not used yet */
/* Set the i8042 real-time clock */
static int hp_sdc_rtc_set_rt (struct timeval *setto)
{
@@ -386,6 +394,7 @@ static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
}
return 0;
}
+#endif
static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) {
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
new file mode 100644
index 000000000000..e204f26b0011
--- /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/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
index 369a39de4ff3..f9179b2585a9 100644
--- a/drivers/input/misc/max8925_onkey.c
+++ b/drivers/input/misc/max8925_onkey.c
@@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev)
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_KEY, KEY_POWER);
- irq[0] += chip->irq_base;
- irq[1] += chip->irq_base;
-
error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-down", info);
if (error < 0) {
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
index 480557f14f23..f3309696d053 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 27c2bc8aa890..b9a05fda03e4 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 0238e0e14335..7c5d72a6a26a 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 5fa99341a39d..b55d5af217a7 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 f3102494237d..ca843b6cf6bd 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 e558a7096618..ecd0547964a5 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 560c243bfcaf..aebfe3ecb945 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -22,7 +22,7 @@ config SERIO_I8042
tristate "i8042 PC Keyboard controller" if EXPERT || !X86
default y
depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
- (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
+ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390
help
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
@@ -36,6 +36,7 @@ config SERIO_I8042
config SERIO_SERPORT
tristate "Serial port line discipline"
default y
+ depends on TTY
help
Say Y here if you plan to use an input device (mouse, joystick,
tablet, 6dof) that communicates over the RS232 serial (COM) port.
@@ -244,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 4b0c8f84f1c1..8edb36c2cdb4 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 000000000000..17e01a807ddc
--- /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 b571eb3e4efc..3fb7727c8ea5 100644
--- a/drivers/input/serio/arc_ps2.c
+++ b/drivers/input/serio/arc_ps2.c
@@ -8,11 +8,13 @@
* Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com>
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#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>
@@ -206,9 +208,9 @@ static int arc_ps2_probe(struct platform_device *pdev)
return -ENOMEM;
}
- arc_ps2->addr = devm_request_and_ioremap(&pdev->dev, res);
- if (!arc_ps2->addr)
- return -EBUSY;
+ arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(arc_ps2->addr))
+ return PTR_ERR(arc_ps2->addr);
dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n",
irq, arc_ps2->addr, ARC_PS2_PORTS);
@@ -258,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 36e799c31f5e..190ce35af7df 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/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index d6cc77a53c7e..5f306f79da0c 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -921,6 +921,7 @@ static int __init i8042_platform_init(void)
int retval;
#ifdef CONFIG_X86
+ u8 a20_on = 0xdf;
/* Just return if pre-detection shows no i8042 controller exist */
if (!x86_platform.i8042_detect())
return -ENODEV;
@@ -960,6 +961,14 @@ static int __init i8042_platform_init(void)
if (dmi_check_system(i8042_dmi_dritek_table))
i8042_dritek = true;
+
+ /*
+ * A20 was already enabled during early kernel init. But some buggy
+ * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to
+ * resume from S3. So we do it here and hope that nothing breaks.
+ */
+ i8042_command(&a20_on, 0x10d1);
+ i8042_command(NULL, 0x00ff); /* Null command for SMM firmware */
#endif /* CONFIG_X86 */
return retval;
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
index 70fe542839fb..436a3433f8e5 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/Kconfig b/drivers/input/touchscreen/Kconfig
index 9a647ee1136a..f9a5fd89bc02 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -517,9 +517,9 @@ config TOUCHSCREEN_TOUCHWIN
To compile this driver as a module, choose M here: the
module will be called touchwin.
-config TOUCHSCREEN_TI_TSCADC
+config TOUCHSCREEN_TI_AM335X_TSC
tristate "TI Touchscreen Interface"
- depends on ARCH_OMAP2PLUS
+ depends on MFD_TI_AM335X_TSCADC
help
Say Y here if you have 4/5/8 wire touchscreen controller
to be connected to the ADC controller on your TI AM335x SoC.
@@ -527,7 +527,7 @@ config TOUCHSCREEN_TI_TSCADC
If unsure, say N.
To compile this driver as a module, choose M here: the
- module will be called ti_tscadc.
+ module will be called ti_am335x_tsc.
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5f949c0bf82f..6bfbeab67c9f 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -51,7 +51,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
-obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o
+obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index 23fa829b869d..f3a174a83c82 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 434c3df250ca..84ccf140c1bb 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 c5c2dbb93869..2c1e46b7e45b 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/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index d04f810cb1dd..59aa24002c7b 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -176,11 +176,17 @@
/* Define for MXT_GEN_COMMAND_T6 */
#define MXT_BOOT_VALUE 0xa5
#define MXT_BACKUP_VALUE 0x55
-#define MXT_BACKUP_TIME 25 /* msec */
-#define MXT_RESET_TIME 65 /* msec */
+#define MXT_BACKUP_TIME 50 /* msec */
+#define MXT_RESET_TIME 200 /* msec */
#define MXT_FWRESET_TIME 175 /* msec */
+/* MXT_SPT_GPIOPWM_T19 field */
+#define MXT_GPIO0_MASK 0x04
+#define MXT_GPIO1_MASK 0x08
+#define MXT_GPIO2_MASK 0x10
+#define MXT_GPIO3_MASK 0x20
+
/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
#define MXT_UNLOCK_CMD_LSB 0xdc
@@ -212,6 +218,8 @@
/* Touchscreen absolute values */
#define MXT_MAX_AREA 0xff
+#define MXT_PIXELS_PER_MM 20
+
struct mxt_info {
u8 family_id;
u8 variant_id;
@@ -243,6 +251,8 @@ struct mxt_data {
const struct mxt_platform_data *pdata;
struct mxt_object *object_table;
struct mxt_info info;
+ bool is_tp;
+
unsigned int irq;
unsigned int max_x;
unsigned int max_y;
@@ -251,6 +261,7 @@ struct mxt_data {
u8 T6_reportid;
u8 T9_reportid_min;
u8 T9_reportid_max;
+ u8 T19_reportid;
};
static bool mxt_object_readable(unsigned int type)
@@ -502,6 +513,21 @@ static int mxt_write_object(struct mxt_data *data,
return mxt_write_reg(data->client, reg + offset, val);
}
+static void mxt_input_button(struct mxt_data *data, struct mxt_message *message)
+{
+ struct input_dev *input = data->input_dev;
+ bool button;
+ int i;
+
+ /* Active-low switch */
+ for (i = 0; i < MXT_NUM_GPIO; i++) {
+ if (data->pdata->key_map[i] == KEY_RESERVED)
+ continue;
+ button = !(message->message[0] & MXT_GPIO0_MASK << i);
+ input_report_key(input, data->pdata->key_map[i], button);
+ }
+}
+
static void mxt_input_touchevent(struct mxt_data *data,
struct mxt_message *message, int id)
{
@@ -585,6 +611,9 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
int id = reportid - data->T9_reportid_min;
mxt_input_touchevent(data, &message, id);
update_input = true;
+ } else if (message.reportid == data->T19_reportid) {
+ mxt_input_button(data, &message);
+ update_input = true;
} else {
mxt_dump_message(dev, &message);
}
@@ -764,6 +793,9 @@ static int mxt_get_object_table(struct mxt_data *data)
data->T9_reportid_min = min_id;
data->T9_reportid_max = max_id;
break;
+ case MXT_SPT_GPIOPWM_T19:
+ data->T19_reportid = min_id;
+ break;
}
}
@@ -777,7 +809,7 @@ static void mxt_free_object_table(struct mxt_data *data)
data->T6_reportid = 0;
data->T9_reportid_min = 0;
data->T9_reportid_max = 0;
-
+ data->T19_reportid = 0;
}
static int mxt_initialize(struct mxt_data *data)
@@ -1115,9 +1147,13 @@ static int mxt_probe(struct i2c_client *client,
goto err_free_mem;
}
- input_dev->name = "Atmel maXTouch Touchscreen";
+ data->is_tp = pdata && pdata->is_tp;
+
+ input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" :
+ "Atmel maXTouch Touchscreen";
snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
client->adapter->nr, client->addr);
+
input_dev->phys = data->phys;
input_dev->id.bustype = BUS_I2C;
@@ -1140,6 +1176,29 @@ static int mxt_probe(struct i2c_client *client,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
+ if (data->is_tp) {
+ int i;
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ for (i = 0; i < MXT_NUM_GPIO; i++)
+ if (pdata->key_map[i] != KEY_RESERVED)
+ __set_bit(pdata->key_map[i], input_dev->keybit);
+
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit);
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+ }
+
/* For single touch */
input_set_abs_params(input_dev, ABS_X,
0, data->max_x, 0, 0);
@@ -1258,6 +1317,7 @@ static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
+ { "atmel_mxt_tp", 0 },
{ "mXT224", 0 },
{ }
};
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 55092d1c5cb9..95f6785a94b0 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -22,7 +22,7 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <mach/board.h>
+#include <linux/platform_data/atmel.h>
#include <mach/cpu.h>
/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
index c6e19a96348e..d3f9f6b0f9b7 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/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
index 303966ffe1e8..8f561e22bdd4 100644
--- a/drivers/input/touchscreen/da9052_tsi.c
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -27,8 +27,6 @@ struct da9052_tsi {
struct input_dev *dev;
struct delayed_work ts_pen_work;
struct mutex mutex;
- unsigned int irq_pendwn;
- unsigned int irq_datardy;
bool stopped;
bool adc_on;
};
@@ -45,8 +43,8 @@ static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
if (!tsi->stopped) {
/* Mask PEN_DOWN event and unmask TSI_READY event */
- disable_irq_nosync(tsi->irq_pendwn);
- enable_irq(tsi->irq_datardy);
+ da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
da9052_ts_adc_toggle(tsi, true);
@@ -137,8 +135,8 @@ static void da9052_ts_pen_work(struct work_struct *work)
return;
/* Mask TSI_READY event and unmask PEN_DOWN event */
- disable_irq(tsi->irq_datardy);
- enable_irq(tsi->irq_pendwn);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
}
}
}
@@ -197,7 +195,7 @@ static int da9052_ts_input_open(struct input_dev *input_dev)
mb();
/* Unmask PEN_DOWN event */
- enable_irq(tsi->irq_pendwn);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
/* Enable Pen Detect Circuit */
return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
@@ -210,11 +208,11 @@ static void da9052_ts_input_close(struct input_dev *input_dev)
tsi->stopped = true;
mb();
- disable_irq(tsi->irq_pendwn);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
cancel_delayed_work_sync(&tsi->ts_pen_work);
if (tsi->adc_on) {
- disable_irq(tsi->irq_datardy);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
da9052_ts_adc_toggle(tsi, false);
/*
@@ -222,7 +220,7 @@ static void da9052_ts_input_close(struct input_dev *input_dev)
* twice and we need to enable it to keep enable/disable
* counter balanced. IRQ is still off though.
*/
- enable_irq(tsi->irq_pendwn);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
}
/* Disable Pen Detect Circuit */
@@ -234,21 +232,12 @@ static int da9052_ts_probe(struct platform_device *pdev)
struct da9052 *da9052;
struct da9052_tsi *tsi;
struct input_dev *input_dev;
- int irq_pendwn;
- int irq_datardy;
int error;
da9052 = dev_get_drvdata(pdev->dev.parent);
if (!da9052)
return -EINVAL;
- irq_pendwn = platform_get_irq_byname(pdev, "PENDWN");
- irq_datardy = platform_get_irq_byname(pdev, "TSIRDY");
- if (irq_pendwn < 0 || irq_datardy < 0) {
- dev_err(da9052->dev, "Unable to determine device interrupts\n");
- return -ENXIO;
- }
-
tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
input_dev = input_allocate_device();
if (!tsi || !input_dev) {
@@ -258,8 +247,6 @@ static int da9052_ts_probe(struct platform_device *pdev)
tsi->da9052 = da9052;
tsi->dev = input_dev;
- tsi->irq_pendwn = da9052->irq_base + irq_pendwn;
- tsi->irq_datardy = da9052->irq_base + irq_datardy;
tsi->stopped = true;
INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
@@ -287,31 +274,25 @@ static int da9052_ts_probe(struct platform_device *pdev)
/* Disable ADC */
da9052_ts_adc_toggle(tsi, false);
- error = request_threaded_irq(tsi->irq_pendwn,
- NULL, da9052_ts_pendwn_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "PENDWN", tsi);
+ error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
+ "pendown-irq", da9052_ts_pendwn_irq, tsi);
if (error) {
dev_err(tsi->da9052->dev,
- "Failed to register PENDWN IRQ %d, error = %d\n",
- tsi->irq_pendwn, error);
+ "Failed to register PENDWN IRQ: %d\n", error);
goto err_free_mem;
}
- error = request_threaded_irq(tsi->irq_datardy,
- NULL, da9052_ts_datardy_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "TSIRDY", tsi);
+ error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
+ "tsiready-irq", da9052_ts_datardy_irq, tsi);
if (error) {
dev_err(tsi->da9052->dev,
- "Failed to register TSIRDY IRQ %d, error = %d\n",
- tsi->irq_datardy, error);
+ "Failed to register TSIRDY IRQ :%d\n", error);
goto err_free_pendwn_irq;
}
/* Mask PEN_DOWN and TSI_READY events */
- disable_irq(tsi->irq_pendwn);
- disable_irq(tsi->irq_datardy);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
error = da9052_configure_tsi(tsi);
if (error)
@@ -326,9 +307,9 @@ static int da9052_ts_probe(struct platform_device *pdev)
return 0;
err_free_datardy_irq:
- free_irq(tsi->irq_datardy, tsi);
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
err_free_pendwn_irq:
- free_irq(tsi->irq_pendwn, tsi);
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
err_free_mem:
kfree(tsi);
input_free_device(input_dev);
@@ -342,8 +323,8 @@ static int da9052_ts_remove(struct platform_device *pdev)
da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
- free_irq(tsi->irq_pendwn, tsi);
- free_irq(tsi->irq_datardy, tsi);
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
input_unregister_device(tsi->dev);
kfree(tsi);
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index a9170157b442..83fa1b15a97f 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 55255a940072..8fe5086c8d2e 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 02103b6abb39..89308fe38752 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 d9d05e222428..1740a2496371 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/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
new file mode 100644
index 000000000000..51e7b87827a4
--- /dev/null
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -0,0 +1,398 @@
+/*
+ * TI Touch Screen driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#define ADCFSM_STEPID 0x10
+#define SEQ_SETTLE 275
+#define MAX_12BIT ((1 << 12) - 1)
+
+struct titsc {
+ struct input_dev *input;
+ struct ti_tscadc_dev *mfd_tscadc;
+ unsigned int irq;
+ unsigned int wires;
+ unsigned int x_plate_resistance;
+ bool pen_down;
+ int steps_to_configure;
+};
+
+static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
+{
+ return readl(ts->mfd_tscadc->tscadc_base + reg);
+}
+
+static void titsc_writel(struct titsc *tsc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, tsc->mfd_tscadc->tscadc_base + reg);
+}
+
+static void titsc_step_config(struct titsc *ts_dev)
+{
+ unsigned int config;
+ int i, total_steps;
+
+ /* Configure the Step registers */
+ total_steps = 2 * ts_dev->steps_to_configure;
+
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | STEPCONFIG_XPP;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
+ break;
+ case 5:
+ config |= STEPCONFIG_YNN |
+ STEPCONFIG_INP_AN4 | STEPCONFIG_XNN |
+ STEPCONFIG_YPP;
+ break;
+ case 8:
+ config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
+ break;
+ }
+
+ for (i = 1; i <= ts_dev->steps_to_configure; i++) {
+ titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | STEPCONFIG_YNN |
+ STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_YPP;
+ break;
+ case 5:
+ config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 |
+ STEPCONFIG_XNP | STEPCONFIG_YPN;
+ break;
+ case 8:
+ config |= STEPCONFIG_YPP;
+ break;
+ }
+
+ for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) {
+ titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ /* Charge step configuration */
+ config = STEPCONFIG_XPP | STEPCONFIG_YNN |
+ STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
+ STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1;
+
+ titsc_writel(ts_dev, REG_CHARGECONFIG, config);
+ titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
+
+ config = 0;
+ /* Configure to calculate pressure */
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | STEPCONFIG_YPP |
+ STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM;
+ titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1),
+ STEPCONFIG_OPENDLY);
+
+ config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1;
+ titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2),
+ STEPCONFIG_OPENDLY);
+
+ titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
+}
+
+static void titsc_read_coordinates(struct titsc *ts_dev,
+ unsigned int *x, unsigned int *y)
+{
+ unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
+ unsigned int prev_val_x = ~0, prev_val_y = ~0;
+ unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
+ unsigned int read, diff;
+ unsigned int i, channel;
+
+ /*
+ * Delta filter is used to remove large variations in sampled
+ * values from ADC. The filter tries to predict where the next
+ * coordinate could be. This is done by taking a previous
+ * coordinate and subtracting it form current one. Further the
+ * algorithm compares the difference with that of a present value,
+ * if true the value is reported to the sub system.
+ */
+ for (i = 0; i < fifocount - 1; i++) {
+ read = titsc_readl(ts_dev, REG_FIFO0);
+ channel = read & 0xf0000;
+ channel = channel >> 0x10;
+ if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) {
+ read &= 0xfff;
+ diff = abs(read - prev_val_x);
+ if (diff < prev_diff_x) {
+ prev_diff_x = diff;
+ *x = read;
+ }
+ prev_val_x = read;
+ }
+
+ read = titsc_readl(ts_dev, REG_FIFO1);
+ channel = read & 0xf0000;
+ channel = channel >> 0x10;
+ if ((channel >= ts_dev->steps_to_configure) &&
+ (channel < (2 * ts_dev->steps_to_configure - 1))) {
+ read &= 0xfff;
+ diff = abs(read - prev_val_y);
+ if (diff < prev_diff_y) {
+ prev_diff_y = diff;
+ *y = read;
+ }
+ prev_val_y = read;
+ }
+ }
+}
+
+static irqreturn_t titsc_irq(int irq, void *dev)
+{
+ struct titsc *ts_dev = dev;
+ struct input_dev *input_dev = ts_dev->input;
+ unsigned int status, irqclr = 0;
+ unsigned int x = 0, y = 0;
+ unsigned int z1, z2, z;
+ unsigned int fsm;
+ unsigned int fifo1count, fifo0count;
+ int i;
+
+ status = titsc_readl(ts_dev, REG_IRQSTATUS);
+ if (status & IRQENB_FIFO0THRES) {
+ titsc_read_coordinates(ts_dev, &x, &y);
+
+ z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff;
+ z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff;
+
+ fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ titsc_readl(ts_dev, REG_FIFO1);
+
+ fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT);
+ for (i = 0; i < fifo0count; i++)
+ titsc_readl(ts_dev, REG_FIFO0);
+
+ if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
+ /*
+ * Calculate pressure using formula
+ * Resistance(touch) = x plate resistance *
+ * x postion/4096 * ((z2 / z1) - 1)
+ */
+ z = z2 - z1;
+ z *= x;
+ z *= ts_dev->x_plate_resistance;
+ z /= z1;
+ z = (z + 2047) >> 12;
+
+ if (z <= MAX_12BIT) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_abs(input_dev, ABS_PRESSURE, z);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ }
+ }
+ irqclr |= IRQENB_FIFO0THRES;
+ }
+
+ /*
+ * Time for sequencer to settle, to read
+ * correct state of the sequencer.
+ */
+ udelay(SEQ_SETTLE);
+
+ status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
+ if (status & IRQENB_PENUP) {
+ /* Pen up event */
+ fsm = titsc_readl(ts_dev, REG_ADCFSM);
+ if (fsm == ADCFSM_STEPID) {
+ ts_dev->pen_down = false;
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ } else {
+ ts_dev->pen_down = true;
+ }
+ irqclr |= IRQENB_PENUP;
+ }
+
+ titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+
+ titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The functions for inserting/removing driver as a module.
+ */
+
+static int titsc_probe(struct platform_device *pdev)
+{
+ struct titsc *ts_dev;
+ struct input_dev *input_dev;
+ struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
+ struct mfd_tscadc_board *pdata;
+ int err;
+
+ pdata = tscadc_dev->dev->platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Could not find platform data\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for device */
+ ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts_dev || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tscadc_dev->tsc = ts_dev;
+ ts_dev->mfd_tscadc = tscadc_dev;
+ ts_dev->input = input_dev;
+ ts_dev->irq = tscadc_dev->irq;
+ ts_dev->wires = pdata->tsc_init->wires;
+ ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance;
+ ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure;
+
+ err = request_irq(ts_dev->irq, titsc_irq,
+ 0, pdev->dev.driver->name, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate irq.\n");
+ goto err_free_mem;
+ }
+
+ titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
+ titsc_step_config(ts_dev);
+ titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure);
+
+ input_dev->name = "ti-tsc";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+
+ /* register to the input system */
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ platform_set_drvdata(pdev, ts_dev);
+ return 0;
+
+err_free_irq:
+ free_irq(ts_dev->irq, ts_dev);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts_dev);
+ return err;
+}
+
+static int titsc_remove(struct platform_device *pdev)
+{
+ struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
+ struct titsc *ts_dev = tscadc_dev->tsc;
+
+ free_irq(ts_dev->irq, ts_dev);
+
+ input_unregister_device(ts_dev->input);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ts_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int titsc_suspend(struct device *dev)
+{
+ struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
+ struct titsc *ts_dev = tscadc_dev->tsc;
+ unsigned int idle;
+
+ if (device_may_wakeup(tscadc_dev->dev)) {
+ idle = titsc_readl(ts_dev, REG_IRQENABLE);
+ titsc_writel(ts_dev, REG_IRQENABLE,
+ (idle | IRQENB_HW_PEN));
+ titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
+ }
+ return 0;
+}
+
+static int titsc_resume(struct device *dev)
+{
+ struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
+ struct titsc *ts_dev = tscadc_dev->tsc;
+
+ if (device_may_wakeup(tscadc_dev->dev)) {
+ titsc_writel(ts_dev, REG_IRQWAKEUP,
+ 0x00);
+ titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+ }
+ titsc_step_config(ts_dev);
+ titsc_writel(ts_dev, REG_FIFO0THR,
+ ts_dev->steps_to_configure);
+ return 0;
+}
+
+static const struct dev_pm_ops titsc_pm_ops = {
+ .suspend = titsc_suspend,
+ .resume = titsc_resume,
+};
+#define TITSC_PM_OPS (&titsc_pm_ops)
+#else
+#define TITSC_PM_OPS NULL
+#endif
+
+static struct platform_driver ti_tsc_driver = {
+ .probe = titsc_probe,
+ .remove = titsc_remove,
+ .driver = {
+ .name = "tsc",
+ .owner = THIS_MODULE,
+ .pm = TITSC_PM_OPS,
+ },
+};
+module_platform_driver(ti_tsc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
deleted file mode 100644
index bcedf2e7468c..000000000000
--- a/drivers/input/touchscreen/ti_tscadc.c
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * TI Touch Screen driver
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/input/ti_tscadc.h>
-#include <linux/delay.h>
-
-#define REG_IRQEOI 0x020
-#define REG_RAWIRQSTATUS 0x024
-#define REG_IRQSTATUS 0x028
-#define REG_IRQENABLE 0x02C
-#define REG_IRQWAKEUP 0x034
-#define REG_CTRL 0x040
-#define REG_ADCFSM 0x044
-#define REG_CLKDIV 0x04C
-#define REG_SE 0x054
-#define REG_IDLECONFIG 0x058
-#define REG_CHARGECONFIG 0x05C
-#define REG_CHARGEDELAY 0x060
-#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8))
-#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8))
-#define REG_STEPCONFIG13 0x0C4
-#define REG_STEPDELAY13 0x0C8
-#define REG_STEPCONFIG14 0x0CC
-#define REG_STEPDELAY14 0x0D0
-#define REG_FIFO0CNT 0xE4
-#define REG_FIFO1THR 0xF4
-#define REG_FIFO0 0x100
-#define REG_FIFO1 0x200
-
-/* Register Bitfields */
-#define IRQWKUP_ENB BIT(0)
-#define STPENB_STEPENB 0x7FFF
-#define IRQENB_FIFO1THRES BIT(5)
-#define IRQENB_PENUP BIT(9)
-#define STEPCONFIG_MODE_HWSYNC 0x2
-#define STEPCONFIG_SAMPLES_AVG (1 << 4)
-#define STEPCONFIG_XPP (1 << 5)
-#define STEPCONFIG_XNN (1 << 6)
-#define STEPCONFIG_YPP (1 << 7)
-#define STEPCONFIG_YNN (1 << 8)
-#define STEPCONFIG_XNP (1 << 9)
-#define STEPCONFIG_YPN (1 << 10)
-#define STEPCONFIG_INM (1 << 18)
-#define STEPCONFIG_INP (1 << 20)
-#define STEPCONFIG_INP_5 (1 << 21)
-#define STEPCONFIG_FIFO1 (1 << 26)
-#define STEPCONFIG_OPENDLY 0xff
-#define STEPCONFIG_Z1 (3 << 19)
-#define STEPIDLE_INP (1 << 22)
-#define STEPCHARGE_RFP (1 << 12)
-#define STEPCHARGE_INM (1 << 15)
-#define STEPCHARGE_INP (1 << 19)
-#define STEPCHARGE_RFM (1 << 23)
-#define STEPCHARGE_DELAY 0x1
-#define CNTRLREG_TSCSSENB (1 << 0)
-#define CNTRLREG_STEPID (1 << 1)
-#define CNTRLREG_STEPCONFIGWRT (1 << 2)
-#define CNTRLREG_4WIRE (1 << 5)
-#define CNTRLREG_5WIRE (1 << 6)
-#define CNTRLREG_8WIRE (3 << 5)
-#define CNTRLREG_TSCENB (1 << 7)
-#define ADCFSM_STEPID 0x10
-
-#define SEQ_SETTLE 275
-#define ADC_CLK 3000000
-#define MAX_12BIT ((1 << 12) - 1)
-#define TSCADC_DELTA_X 15
-#define TSCADC_DELTA_Y 15
-
-struct tscadc {
- struct input_dev *input;
- struct clk *tsc_ick;
- void __iomem *tsc_base;
- unsigned int irq;
- unsigned int wires;
- unsigned int x_plate_resistance;
- bool pen_down;
-};
-
-static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg)
-{
- return readl(ts->tsc_base + reg);
-}
-
-static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
- unsigned int val)
-{
- writel(val, tsc->tsc_base + reg);
-}
-
-static void tscadc_step_config(struct tscadc *ts_dev)
-{
- unsigned int config;
- int i;
-
- /* Configure the Step registers */
-
- config = STEPCONFIG_MODE_HWSYNC |
- STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP;
- switch (ts_dev->wires) {
- case 4:
- config |= STEPCONFIG_INP | STEPCONFIG_XNN;
- break;
- case 5:
- config |= STEPCONFIG_YNN |
- STEPCONFIG_INP_5 | STEPCONFIG_XNN |
- STEPCONFIG_YPP;
- break;
- case 8:
- config |= STEPCONFIG_INP | STEPCONFIG_XNN;
- break;
- }
-
- for (i = 1; i < 7; i++) {
- tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
- tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
- }
-
- config = 0;
- config = STEPCONFIG_MODE_HWSYNC |
- STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN |
- STEPCONFIG_INM | STEPCONFIG_FIFO1;
- switch (ts_dev->wires) {
- case 4:
- config |= STEPCONFIG_YPP;
- break;
- case 5:
- config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 |
- STEPCONFIG_XNP | STEPCONFIG_YPN;
- break;
- case 8:
- config |= STEPCONFIG_YPP;
- break;
- }
-
- for (i = 7; i < 13; i++) {
- tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
- tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
- }
-
- config = 0;
- /* Charge step configuration */
- config = STEPCONFIG_XPP | STEPCONFIG_YNN |
- STEPCHARGE_RFP | STEPCHARGE_RFM |
- STEPCHARGE_INM | STEPCHARGE_INP;
-
- tscadc_writel(ts_dev, REG_CHARGECONFIG, config);
- tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY);
-
- config = 0;
- /* Configure to calculate pressure */
- config = STEPCONFIG_MODE_HWSYNC |
- STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP |
- STEPCONFIG_XNN | STEPCONFIG_INM;
- tscadc_writel(ts_dev, REG_STEPCONFIG13, config);
- tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY);
-
- config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1;
- tscadc_writel(ts_dev, REG_STEPCONFIG14, config);
- tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY);
-
- tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
-}
-
-static void tscadc_idle_config(struct tscadc *ts_config)
-{
- unsigned int idleconfig;
-
- idleconfig = STEPCONFIG_YNN |
- STEPCONFIG_INM |
- STEPCONFIG_YPN | STEPIDLE_INP;
- tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig);
-}
-
-static void tscadc_read_coordinates(struct tscadc *ts_dev,
- unsigned int *x, unsigned int *y)
-{
- unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT);
- unsigned int prev_val_x = ~0, prev_val_y = ~0;
- unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
- unsigned int read, diff;
- unsigned int i;
-
- /*
- * Delta filter is used to remove large variations in sampled
- * values from ADC. The filter tries to predict where the next
- * coordinate could be. This is done by taking a previous
- * coordinate and subtracting it form current one. Further the
- * algorithm compares the difference with that of a present value,
- * if true the value is reported to the sub system.
- */
- for (i = 0; i < fifocount - 1; i++) {
- read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
- diff = abs(read - prev_val_x);
- if (diff < prev_diff_x) {
- prev_diff_x = diff;
- *x = read;
- }
- prev_val_x = read;
-
- read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
- diff = abs(read - prev_val_y);
- if (diff < prev_diff_y) {
- prev_diff_y = diff;
- *y = read;
- }
- prev_val_y = read;
- }
-}
-
-static irqreturn_t tscadc_irq(int irq, void *dev)
-{
- struct tscadc *ts_dev = dev;
- struct input_dev *input_dev = ts_dev->input;
- unsigned int status, irqclr = 0;
- unsigned int x = 0, y = 0;
- unsigned int z1, z2, z;
- unsigned int fsm;
-
- status = tscadc_readl(ts_dev, REG_IRQSTATUS);
- if (status & IRQENB_FIFO1THRES) {
- tscadc_read_coordinates(ts_dev, &x, &y);
-
- z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
- z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
-
- if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
- /*
- * Calculate pressure using formula
- * Resistance(touch) = x plate resistance *
- * x postion/4096 * ((z2 / z1) - 1)
- */
- z = z2 - z1;
- z *= x;
- z *= ts_dev->x_plate_resistance;
- z /= z1;
- z = (z + 2047) >> 12;
-
- if (z <= MAX_12BIT) {
- input_report_abs(input_dev, ABS_X, x);
- input_report_abs(input_dev, ABS_Y, y);
- input_report_abs(input_dev, ABS_PRESSURE, z);
- input_report_key(input_dev, BTN_TOUCH, 1);
- input_sync(input_dev);
- }
- }
- irqclr |= IRQENB_FIFO1THRES;
- }
-
- /*
- * Time for sequencer to settle, to read
- * correct state of the sequencer.
- */
- udelay(SEQ_SETTLE);
-
- status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS);
- if (status & IRQENB_PENUP) {
- /* Pen up event */
- fsm = tscadc_readl(ts_dev, REG_ADCFSM);
- if (fsm == ADCFSM_STEPID) {
- ts_dev->pen_down = false;
- input_report_key(input_dev, BTN_TOUCH, 0);
- input_report_abs(input_dev, ABS_PRESSURE, 0);
- input_sync(input_dev);
- } else {
- ts_dev->pen_down = true;
- }
- irqclr |= IRQENB_PENUP;
- }
-
- tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr);
- /* check pending interrupts */
- tscadc_writel(ts_dev, REG_IRQEOI, 0x0);
-
- tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
- return IRQ_HANDLED;
-}
-
-/*
- * The functions for inserting/removing driver as a module.
- */
-
-static int tscadc_probe(struct platform_device *pdev)
-{
- const struct tsc_data *pdata = pdev->dev.platform_data;
- struct resource *res;
- struct tscadc *ts_dev;
- struct input_dev *input_dev;
- struct clk *clk;
- int err;
- int clk_value, ctrl, irq;
-
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform data.\n");
- return -EINVAL;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no memory resource defined.\n");
- return -EINVAL;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq ID is specified.\n");
- return -EINVAL;
- }
-
- /* Allocate memory for device */
- ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ts_dev || !input_dev) {
- dev_err(&pdev->dev, "failed to allocate memory.\n");
- err = -ENOMEM;
- goto err_free_mem;
- }
-
- ts_dev->input = input_dev;
- ts_dev->irq = irq;
- ts_dev->wires = pdata->wires;
- ts_dev->x_plate_resistance = pdata->x_plate_resistance;
-
- res = request_mem_region(res->start, resource_size(res), pdev->name);
- if (!res) {
- dev_err(&pdev->dev, "failed to reserve registers.\n");
- err = -EBUSY;
- goto err_free_mem;
- }
-
- ts_dev->tsc_base = ioremap(res->start, resource_size(res));
- if (!ts_dev->tsc_base) {
- dev_err(&pdev->dev, "failed to map registers.\n");
- err = -ENOMEM;
- goto err_release_mem_region;
- }
-
- err = request_irq(ts_dev->irq, tscadc_irq,
- 0, pdev->dev.driver->name, ts_dev);
- if (err) {
- dev_err(&pdev->dev, "failed to allocate irq.\n");
- goto err_unmap_regs;
- }
-
- ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick");
- if (IS_ERR(ts_dev->tsc_ick)) {
- dev_err(&pdev->dev, "failed to get TSC ick\n");
- goto err_free_irq;
- }
- clk_enable(ts_dev->tsc_ick);
-
- clk = clk_get(&pdev->dev, "adc_tsc_fck");
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "failed to get TSC fck\n");
- err = PTR_ERR(clk);
- goto err_disable_clk;
- }
-
- clk_value = clk_get_rate(clk) / ADC_CLK;
- clk_put(clk);
-
- if (clk_value < 7) {
- dev_err(&pdev->dev, "clock input less than min clock requirement\n");
- goto err_disable_clk;
- }
- /* CLKDIV needs to be configured to the value minus 1 */
- tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1);
-
- /* Enable wake-up of the SoC using touchscreen */
- tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
-
- ctrl = CNTRLREG_STEPCONFIGWRT |
- CNTRLREG_TSCENB |
- CNTRLREG_STEPID;
- switch (ts_dev->wires) {
- case 4:
- ctrl |= CNTRLREG_4WIRE;
- break;
- case 5:
- ctrl |= CNTRLREG_5WIRE;
- break;
- case 8:
- ctrl |= CNTRLREG_8WIRE;
- break;
- }
- tscadc_writel(ts_dev, REG_CTRL, ctrl);
-
- tscadc_idle_config(ts_dev);
- tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
- tscadc_step_config(ts_dev);
- tscadc_writel(ts_dev, REG_FIFO1THR, 6);
-
- ctrl |= CNTRLREG_TSCSSENB;
- tscadc_writel(ts_dev, REG_CTRL, ctrl);
-
- input_dev->name = "ti-tsc-adc";
- input_dev->dev.parent = &pdev->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
- input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
-
- /* register to the input system */
- err = input_register_device(input_dev);
- if (err)
- goto err_disable_clk;
-
- platform_set_drvdata(pdev, ts_dev);
- return 0;
-
-err_disable_clk:
- clk_disable(ts_dev->tsc_ick);
- clk_put(ts_dev->tsc_ick);
-err_free_irq:
- free_irq(ts_dev->irq, ts_dev);
-err_unmap_regs:
- iounmap(ts_dev->tsc_base);
-err_release_mem_region:
- release_mem_region(res->start, resource_size(res));
-err_free_mem:
- input_free_device(input_dev);
- kfree(ts_dev);
- return err;
-}
-
-static int __devexit tscadc_remove(struct platform_device *pdev)
-{
- struct tscadc *ts_dev = platform_get_drvdata(pdev);
- struct resource *res;
-
- free_irq(ts_dev->irq, ts_dev);
-
- input_unregister_device(ts_dev->input);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iounmap(ts_dev->tsc_base);
- release_mem_region(res->start, resource_size(res));
-
- clk_disable(ts_dev->tsc_ick);
- clk_put(ts_dev->tsc_ick);
-
- kfree(ts_dev);
-
- platform_set_drvdata(pdev, NULL);
- return 0;
-}
-
-static struct platform_driver ti_tsc_driver = {
- .probe = tscadc_probe,
- .remove = __devexit_p(tscadc_remove),
- .driver = {
- .name = "tsc",
- .owner = THIS_MODULE,
- },
-};
-module_platform_driver(ti_tsc_driver);
-
-MODULE_DESCRIPTION("TI touchscreen controller driver");
-MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
index 6e743e3dfda4..16b52115c27f 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 5dbe73af2f8f..7e45c9f6e6b7 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;
}