Commit 251df49d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:
 "Assorted fixes and cleanups to the existing drivers plus a new driver
  for IMS Passenger Control Unit device they use for ther in-flight
  entertainment system."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (44 commits)
  Input: trackpoint - Optimize trackpoint init to use power-on reset
  Input: apbps2 - convert to devm_ioremap_resource()
  Input: ALPS - use %ph to print buffers
  ARM - shmobile: Armadillo800EVA: Move st1232 reset pin handling
  Input: st1232 - add reset pin handling
  Input: st1232 - convert to devm_* infrastructure
  Input: MT - handle semi-mt devices in core
  Input: adxl34x - use spi_get_drvdata()
  Input: ad7877 - use spi_get_drvdata() and spi_set_drvdata()
  Input: ads7846 - use spi_get_drvdata() and spi_set_drvdata()
  Input: ims-pcu - fix a memory leak on error
  Input: sysrq - supplement reset sequence with timeout functionality
  Input: tegra-kbc - support for defining row/columns based on SoC
  Input: imx_keypad - switch to using managed resources
  Input: arc_ps2 - add support for device tree
  Input: mma8450 - fix signed 12bits to 32bits conversion
  Input: eeti_ts - remove redundant null check
  Input: edt-ft5x06 - remove redundant null check before kfree
  Input: ad714x - add CONFIG_PM_SLEEP to suspend/resume functions
  Input: adxl34x - add CONFIG_PM_SLEEP to suspend/resume functions
  ...
parents 8a72f382 bf61c884
Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse.
The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library.
Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system,
these properties are built from information in the AMBA plug&play and from
bootloader settings.
Required properties:
- name : Should be "GAISLER_APBPS2" or "01_060"
- reg : Address and length of the register set for the device
- interrupts : Interrupt numbers for this device
For further information look in the documentation for the GLIB IP core library:
http://www.gaisler.com/products/grlib/grip.pdf
* AUO in-cell touchscreen controller using Pixcir sensors
Required properties:
- compatible: must be "auo,auo_pixcir_ts"
- reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected
- gpios: gpios the chip is connected to
first one is the interrupt gpio and second one the reset gpio
- x-size: horizontal resolution of touchscreen
- y-size: vertical resolution of touchscreen
Example:
i2c@00000000 {
/* ... */
auo_pixcir_ts@5c {
compatible = "auo,auo_pixcir_ts";
reg = <0x5c>;
interrupts = <2 0>;
gpios = <&gpf 2 0 2>, /* INT */
<&gpf 5 1 0>; /* RST */
x-size = <800>;
y-size = <600>;
};
/* ... */
};
* Sitronix st1232 touchscreen controller
Required properties:
- compatible: must be "sitronix,st1232"
- reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected
Optional properties:
- gpios: a phandle to the reset GPIO
Example:
i2c@00000000 {
/* ... */
touchscreen@55 {
compatible = "sitronix,st1232";
reg = <0x55>;
interrupts = <2 0>;
gpios = <&gpio1 166 0>;
};
/* ... */
};
* ARC PS/2 driver: PS/2 block used in some ARC FPGA's & nSIM OSCI model
Required properties:
- compatible : "snps,arc_ps2"
- reg : offset and length (always 0x14) of registers
- interrupts : interrupt
- interrupt-names : name of interrupt, must be "arc_ps2_irq"
Example:
serio@c9000400 {
compatible = "snps,arc_ps2";
reg = <0xc9000400 0x14>;
interrupts = <13>;
interrupt-names = "arc_ps2_irq";
}
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/platform_data/st1232_pdata.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/gpio.h> #include <linux/gpio.h>
...@@ -882,10 +883,15 @@ static struct platform_device i2c_gpio_device = { ...@@ -882,10 +883,15 @@ static struct platform_device i2c_gpio_device = {
}; };
/* I2C */ /* I2C */
static struct st1232_pdata st1232_i2c0_pdata = {
.reset_gpio = 166,
};
static struct i2c_board_info i2c0_devices[] = { static struct i2c_board_info i2c0_devices[] = {
{ {
I2C_BOARD_INFO("st1232-ts", 0x55), I2C_BOARD_INFO("st1232-ts", 0x55),
.irq = evt2irq(0x0340), .irq = evt2irq(0x0340),
.platform_data = &st1232_i2c0_pdata,
}, },
{ {
I2C_BOARD_INFO("wm8978", 0x1a), I2C_BOARD_INFO("wm8978", 0x1a),
...@@ -1009,7 +1015,6 @@ static void __init eva_init(void) ...@@ -1009,7 +1015,6 @@ static void __init eva_init(void)
/* Touchscreen */ /* Touchscreen */
gpio_request(GPIO_FN_IRQ10, NULL); /* TP_INT */ gpio_request(GPIO_FN_IRQ10, NULL); /* TP_INT */
gpio_request_one(GPIO_PORT166, GPIOF_OUT_INIT_HIGH, NULL); /* TP_RST_B */
/* GETHER */ /* GETHER */
gpio_request(GPIO_FN_ET_CRS, NULL); gpio_request(GPIO_FN_ET_CRS, NULL);
......
...@@ -670,6 +670,80 @@ int devres_release_group(struct device *dev, void *id) ...@@ -670,6 +670,80 @@ int devres_release_group(struct device *dev, void *id)
} }
EXPORT_SYMBOL_GPL(devres_release_group); EXPORT_SYMBOL_GPL(devres_release_group);
/*
* Custom devres actions allow inserting a simple function call
* into the teadown sequence.
*/
struct action_devres {
void *data;
void (*action)(void *);
};
static int devm_action_match(struct device *dev, void *res, void *p)
{
struct action_devres *devres = res;
struct action_devres *target = p;
return devres->action == target->action &&
devres->data == target->data;
}
static void devm_action_release(struct device *dev, void *res)
{
struct action_devres *devres = res;
devres->action(devres->data);
}
/**
* devm_add_action() - add a custom action to list of managed resources
* @dev: Device that owns the action
* @action: Function that should be called
* @data: Pointer to data passed to @action implementation
*
* This adds a custom action to the list of managed resources so that
* it gets executed as part of standard resource unwinding.
*/
int devm_add_action(struct device *dev, void (*action)(void *), void *data)
{
struct action_devres *devres;
devres = devres_alloc(devm_action_release,
sizeof(struct action_devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->data = data;
devres->action = action;
devres_add(dev, devres);
return 0;
}
EXPORT_SYMBOL_GPL(devm_add_action);
/**
* devm_remove_action() - removes previously added custom action
* @dev: Device that owns the action
* @action: Function implementing the action
* @data: Pointer to data passed to @action implementation
*
* Removes instance of @action previously added by devm_add_action().
* Both action and data should match one of the existing entries.
*/
void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
{
struct action_devres devres = {
.data = data,
.action = action,
};
WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
&devres));
}
EXPORT_SYMBOL_GPL(devm_remove_action);
/* /*
* Managed kzalloc/kfree * Managed kzalloc/kfree
*/ */
......
...@@ -79,6 +79,8 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, ...@@ -79,6 +79,8 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
} }
if (flags & INPUT_MT_DIRECT) if (flags & INPUT_MT_DIRECT)
__set_bit(INPUT_PROP_DIRECT, dev->propbit); __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) { if (flags & INPUT_MT_TRACK) {
unsigned int n2 = num_slots * num_slots; unsigned int n2 = num_slots * num_slots;
mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL); mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
...@@ -246,6 +248,7 @@ void input_mt_sync_frame(struct input_dev *dev) ...@@ -246,6 +248,7 @@ void input_mt_sync_frame(struct input_dev *dev)
{ {
struct input_mt *mt = dev->mt; struct input_mt *mt = dev->mt;
struct input_mt_slot *s; struct input_mt_slot *s;
bool use_count = false;
if (!mt) if (!mt)
return; return;
...@@ -259,7 +262,10 @@ void input_mt_sync_frame(struct input_dev *dev) ...@@ -259,7 +262,10 @@ void input_mt_sync_frame(struct input_dev *dev)
} }
} }
input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER)); if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT))
use_count = true;
input_mt_report_pointer_emulation(dev, use_count);
mt->frame++; mt->frame++;
} }
......
...@@ -260,18 +260,6 @@ static struct platform_driver amikbd_driver = { ...@@ -260,18 +260,6 @@ static struct platform_driver amikbd_driver = {
}, },
}; };
static int __init amikbd_init(void) module_platform_driver_probe(amikbd_driver, amikbd_probe);
{
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_ALIAS("platform:amiga-keyboard"); MODULE_ALIAS("platform:amiga-keyboard");
...@@ -329,17 +329,7 @@ static struct platform_driver davinci_ks_driver = { ...@@ -329,17 +329,7 @@ static struct platform_driver davinci_ks_driver = {
.remove = davinci_ks_remove, .remove = davinci_ks_remove,
}; };
static int __init davinci_ks_init(void) module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe);
{
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_AUTHOR("Miguel Aguilar"); MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
......
...@@ -448,24 +448,17 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -448,24 +448,17 @@ static int imx_keypad_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
res = request_mem_region(res->start, resource_size(res), pdev->name); input_dev = devm_input_allocate_device(&pdev->dev);
if (res == NULL) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
return -EBUSY;
}
input_dev = input_allocate_device();
if (!input_dev) { if (!input_dev) {
dev_err(&pdev->dev, "failed to allocate the input device\n"); dev_err(&pdev->dev, "failed to allocate the input device\n");
error = -ENOMEM; return -ENOMEM;
goto failed_rel_mem;
} }
keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL); keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
GFP_KERNEL);
if (!keypad) { if (!keypad) {
dev_err(&pdev->dev, "not enough memory for driver data\n"); dev_err(&pdev->dev, "not enough memory for driver data\n");
error = -ENOMEM; return -ENOMEM;
goto failed_free_input;
} }
keypad->input_dev = input_dev; keypad->input_dev = input_dev;
...@@ -475,18 +468,14 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -475,18 +468,14 @@ static int imx_keypad_probe(struct platform_device *pdev)
setup_timer(&keypad->check_matrix_timer, setup_timer(&keypad->check_matrix_timer,
imx_keypad_check_for_events, (unsigned long) keypad); imx_keypad_check_for_events, (unsigned long) keypad);
keypad->mmio_base = ioremap(res->start, resource_size(res)); keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (keypad->mmio_base == NULL) { if (IS_ERR(keypad->mmio_base))
dev_err(&pdev->dev, "failed to remap I/O memory\n"); return PTR_ERR(keypad->mmio_base);
error = -ENOMEM;
goto failed_free_priv;
}
keypad->clk = clk_get(&pdev->dev, NULL); keypad->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) { if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n"); dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk); return PTR_ERR(keypad->clk);
goto failed_unmap;
} }
/* Init the Input device */ /* Init the Input device */
...@@ -502,7 +491,7 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -502,7 +491,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
keypad->keycodes, input_dev); keypad->keycodes, input_dev);
if (error) { if (error) {
dev_err(&pdev->dev, "failed to build keymap\n"); dev_err(&pdev->dev, "failed to build keymap\n");
goto failed_clock_put; return error;
} }
/* Search for rows and cols enabled */ /* Search for rows and cols enabled */
...@@ -527,60 +516,23 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -527,60 +516,23 @@ static int imx_keypad_probe(struct platform_device *pdev)
imx_keypad_inhibit(keypad); imx_keypad_inhibit(keypad);
clk_disable_unprepare(keypad->clk); 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); pdev->name, keypad);
if (error) { if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n"); dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_clock_put; return error;
} }
/* Register the input device */ /* Register the input device */
error = input_register_device(input_dev); error = input_register_device(input_dev);
if (error) { if (error) {
dev_err(&pdev->dev, "failed to register input device\n"); dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_irq; return error;
} }
platform_set_drvdata(pdev, keypad); platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1); 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; return 0;
} }
...@@ -640,7 +592,6 @@ static struct platform_driver imx_keypad_driver = { ...@@ -640,7 +592,6 @@ static struct platform_driver imx_keypad_driver = {
.of_match_table = of_match_ptr(imx_keypad_of_match), .of_match_table = of_match_ptr(imx_keypad_of_match),
}, },
.probe = imx_keypad_probe, .probe = imx_keypad_probe,
.remove = imx_keypad_remove,
}; };
module_platform_driver(imx_keypad_driver); module_platform_driver(imx_keypad_driver);
......
...@@ -430,17 +430,7 @@ static struct platform_driver ske_keypad_driver = { ...@@ -430,17 +430,7 @@ static struct platform_driver ske_keypad_driver = {
.remove = ske_keypad_remove, .remove = ske_keypad_remove,
}; };
static int __init ske_keypad_init(void) module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe);
{
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_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>"); MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
......
...@@ -27,17 +27,19 @@ ...@@ -27,17 +27,19 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/clk/tegra.h> #include <linux/clk/tegra.h>
#include <linux/err.h>
#define KBC_MAX_GPIO 24
#define KBC_MAX_KPENT 8 #define KBC_MAX_KPENT 8
#define KBC_MAX_ROW 16 /* Maximum row/column supported by Tegra KBC yet is 16x8 */
#define KBC_MAX_COL 8 #define KBC_MAX_GPIO 24
#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) /* Maximum keys supported by Tegra KBC yet is 16 x 8*/
#define KBC_MAX_KEY (16 * 8)
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu #define KBC_MAX_DEBOUNCE_CNT 0x3ffu
...@@ -80,6 +82,12 @@ enum tegra_pin_type { ...@@ -80,6 +82,12 @@ enum tegra_pin_type {
PIN_CFG_ROW, PIN_CFG_ROW,
}; };
/* Tegra KBC hw support */
struct tegra_kbc_hw_support {
int max_rows;
int max_columns;
};
struct tegra_kbc_pin_cfg { struct tegra_kbc_pin_cfg {
enum tegra_pin_type type; enum tegra_pin_type type;
unsigned char num; unsigned char num;
...@@ -108,6 +116,9 @@ struct tegra_kbc { ...@@ -108,6 +116,9 @@ struct tegra_kbc {
u32 wakeup_key; u32 wakeup_key;
struct timer_list timer; struct timer_list timer;
struct clk *clk; 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, 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) ...@@ -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. * 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) { if (fn_keypress) {
for (i = 0; i < num_down; i++) { for (i = 0; i < num_down; i++) {
scancodes[i] += KBC_MAX_KEY; scancodes[i] += kbc->max_keys;
keycodes[i] = kbc->keycode[scancodes[i]]; keycodes[i] = kbc->keycode[scancodes[i]];
} }
} }
...@@ -315,7 +326,7 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) ...@@ -315,7 +326,7 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
/* Either mask all keys or none. */ /* Either mask all keys or none. */
rst_val = (filter && !kbc->wakeup) ? ~0 : 0; 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); 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, ...@@ -452,7 +463,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
switch (pin_cfg->type) { switch (pin_cfg->type) {
case PIN_CFG_ROW: case PIN_CFG_ROW:
if (pin_cfg->num >= KBC_MAX_ROW) { if (pin_cfg->num >= kbc->hw_support->max_rows) {
dev_err(kbc->dev, dev_err(kbc->dev,
"pin_cfg[%d]: invalid row number %d\n", "pin_cfg[%d]: invalid row number %d\n",
i, pin_cfg->num); i, pin_cfg->num);
...@@ -462,7 +473,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, ...@@ -462,7 +473,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
break; break;
case PIN_CFG_COL: case PIN_CFG_COL:
if (pin_cfg->num >= KBC_MAX_COL) { if (pin_cfg->num >= kbc->hw_support->max_columns) {
dev_err(kbc->dev, dev_err(kbc->dev,
"pin_cfg[%d]: invalid column number %d\n", "pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num); i, pin_cfg->num);
...@@ -520,6 +531,18 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) ...@@ -520,6 +531,18 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
} }
num_cols = proplen / sizeof(u32); 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)) { if (!of_get_property(np, "linux,keymap", &proplen)) {
dev_err(kbc->dev, "property linux,keymap not found\n"); dev_err(kbc->dev, "property linux,keymap not found\n");
return -ENOENT; return -ENOENT;
...@@ -532,7 +555,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) ...@@ -532,7 +555,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
} }
/* Set all pins as non-configured */ /* 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; kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", 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) ...@@ -562,6 +585,24 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
return 0; 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) static int tegra_kbc_probe(struct platform_device *pdev)
{ {
struct tegra_kbc *kbc; struct tegra_kbc *kbc;
...@@ -570,7 +611,10 @@ static int tegra_kbc_probe(struct platform_device *pdev) ...@@ -570,7 +611,10 @@ static int tegra_kbc_probe(struct platform_device *pdev)
int num_rows = 0; int num_rows = 0;
unsigned int debounce_cnt; unsigned int debounce_cnt;
unsigned int scan_time_rows; 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); kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
if (!kbc) { if (!kbc) {
...@@ -579,6 +623,12 @@ static int tegra_kbc_probe(struct platform_device *pdev) ...@@ -579,6 +623,12 @@ static int tegra_kbc_probe(struct platform_device *pdev)
} }
kbc->dev = &pdev->dev; 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); spin_lock_init(&kbc->lock);
err = tegra_kbc_parse_dt(kbc); err = tegra_kbc_parse_dt(kbc);
...@@ -608,11 +658,9 @@ static int tegra_kbc_probe(struct platform_device *pdev) ...@@ -608,11 +658,9 @@ static int tegra_kbc_probe(struct platform_device *pdev)
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
kbc->mmio = devm_request_and_ioremap(&pdev->dev, res); kbc->mmio = devm_ioremap_resource(&pdev->dev, res);
if (!kbc->mmio) { if (IS_ERR(kbc->mmio))
dev_err(&pdev->dev, "Cannot request memregion/iomap address\n"); return PTR_ERR(kbc->mmio);
return -EBUSY;
}
kbc->clk = devm_clk_get(&pdev->dev, NULL); kbc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbc->clk)) { if (IS_ERR(kbc->clk)) {
...@@ -641,7 +689,8 @@ static int tegra_kbc_probe(struct platform_device *pdev) ...@@ -641,7 +689,8 @@ static int tegra_kbc_probe(struct platform_device *pdev)
keymap_rows *= 2; keymap_rows *= 2;
err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, 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); kbc->keycode, kbc->idev);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to setup keymap\n"); dev_err(&pdev->dev, "failed to setup keymap\n");
...@@ -767,12 +816,6 @@ static int tegra_kbc_resume(struct device *dev) ...@@ -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 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 = { static struct platform_driver tegra_kbc_driver = {
.probe = tegra_kbc_probe, .probe = tegra_kbc_probe,
.driver = { .driver = {
......
...@@ -590,6 +590,16 @@ config INPUT_ADXL34X_SPI ...@@ -590,6 +590,16 @@ config INPUT_ADXL34X_SPI
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called adxl34x-spi. 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 config INPUT_CMA3000
tristate "VTI CMA3000 Tri-axis accelerometer" tristate "VTI CMA3000 Tri-axis accelerometer"
help help
......
...@@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o ...@@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.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_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include "ad714x.h" #include "ad714x.h"
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int ad714x_i2c_suspend(struct device *dev) static int ad714x_i2c_suspend(struct device *dev)
{ {
return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev))); return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev)));
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */ #define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */
#define AD714x_SPI_READ BIT(10) #define AD714x_SPI_READ BIT(10)
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int ad714x_spi_suspend(struct device *dev) static int ad714x_spi_suspend(struct device *dev)
{ {
return ad714x_disable(spi_get_drvdata(to_spi_device(dev))); return ad714x_disable(spi_get_drvdata(to_spi_device(dev)));
......
...@@ -105,7 +105,7 @@ static int adxl34x_i2c_remove(struct i2c_client *client) ...@@ -105,7 +105,7 @@ static int adxl34x_i2c_remove(struct i2c_client *client)
return adxl34x_remove(ac); return adxl34x_remove(ac);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int adxl34x_i2c_suspend(struct device *dev) static int adxl34x_i2c_suspend(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
......
...@@ -89,16 +89,16 @@ static int adxl34x_spi_probe(struct spi_device *spi) ...@@ -89,16 +89,16 @@ static int adxl34x_spi_probe(struct spi_device *spi)
static int adxl34x_spi_remove(struct spi_device *spi) static int adxl34x_spi_remove(struct spi_device *spi)
{ {
struct adxl34x *ac = dev_get_drvdata(&spi->dev); struct adxl34x *ac = spi_get_drvdata(spi);
return adxl34x_remove(ac); return adxl34x_remove(ac);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int adxl34x_spi_suspend(struct device *dev) static int adxl34x_spi_suspend(struct device *dev)
{ {
struct spi_device *spi = to_spi_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); adxl34x_suspend(ac);
...@@ -108,7 +108,7 @@ static int adxl34x_spi_suspend(struct device *dev) ...@@ -108,7 +108,7 @@ static int adxl34x_spi_suspend(struct device *dev)
static int adxl34x_spi_resume(struct device *dev) static int adxl34x_spi_resume(struct device *dev)
{ {
struct spi_device *spi = to_spi_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); adxl34x_resume(ac);
......
/*
* 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");
...@@ -123,9 +123,9 @@ static void mma8450_poll(struct input_polled_dev *dev) ...@@ -123,9 +123,9 @@ static void mma8450_poll(struct input_polled_dev *dev)
if (ret < 0) if (ret < 0)
return; return;
x = ((buf[1] << 4) & 0xff0) | (buf[0] & 0xf); x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf);
y = ((buf[3] << 4) & 0xff0) | (buf[2] & 0xf); y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf);
z = ((buf[5] << 4) & 0xff0) | (buf[4] & 0xf); z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf);
input_report_abs(dev->input, ABS_X, x); input_report_abs(dev->input, ABS_X, x);
input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_Y, y);
......
...@@ -114,18 +114,8 @@ static struct platform_driver twl4030_pwrbutton_driver = { ...@@ -114,18 +114,8 @@ static struct platform_driver twl4030_pwrbutton_driver = {
}, },
}; };
static int __init twl4030_pwrbutton_init(void) module_platform_driver_probe(twl4030_pwrbutton_driver,
{
return platform_driver_probe(&twl4030_pwrbutton_driver,
twl4030_pwrbutton_probe); 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_ALIAS("platform:twl4030_pwrbutton");
MODULE_DESCRIPTION("Triton2 Power Button"); MODULE_DESCRIPTION("Triton2 Power Button");
......
...@@ -1013,8 +1013,8 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, ...@@ -1013,8 +1013,8 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -EIO; return -EIO;
psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n", psmouse_dbg(psmouse, "%2.2X report: %3ph\n",
repeated_command, param[0], param[1], param[2]); repeated_command, param);
return 0; return 0;
} }
...@@ -1274,9 +1274,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) ...@@ -1274,9 +1274,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
psmouse_warn(psmouse, "trackstick E7 report failed\n"); psmouse_warn(psmouse, "trackstick E7 report failed\n");
ret = -ENODEV; ret = -ENODEV;
} else { } else {
psmouse_dbg(psmouse, psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
"trackstick E7 report: %2.2x %2.2x %2.2x\n",
param[0], param[1], param[2]);
/* /*
* Not sure what this does, but it is absolutely * Not sure what this does, but it is absolutely
...@@ -1323,6 +1321,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse) ...@@ -1323,6 +1321,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
if (reg_val == -EIO) if (reg_val == -EIO)
goto error; goto error;
if (reg_val == 0 && if (reg_val == 0 &&
alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
goto error; goto error;
...@@ -1676,8 +1675,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) ...@@ -1676,8 +1675,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
} }
psmouse_info(psmouse, psmouse_info(psmouse,
"Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n", "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]);
return -EINVAL; return -EINVAL;
} }
......
...@@ -146,18 +146,6 @@ static struct platform_driver amimouse_driver = { ...@@ -146,18 +146,6 @@ static struct platform_driver amimouse_driver = {
}, },
}; };
static int __init amimouse_init(void) module_platform_driver_probe(amimouse_driver, amimouse_probe);
{
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_ALIAS("platform:amiga-mouse"); MODULE_ALIAS("platform:amiga-mouse");
...@@ -19,10 +19,35 @@ ...@@ -19,10 +19,35 @@
#include "psmouse.h" #include "psmouse.h"
#include "trackpoint.h" #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 * 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)) || if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { 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 ...@@ -32,7 +57,8 @@ static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned ch
return 0; 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)) || if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) || 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 ...@@ -44,7 +70,8 @@ static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned c
return 0; 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 */ /* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F) if (loc < 0x20 || loc >= 0x2F)
...@@ -60,6 +87,18 @@ static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsig ...@@ -60,6 +87,18 @@ static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsig
return 0; 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 * Trackpoint-specific attributes
...@@ -69,6 +108,7 @@ struct trackpoint_attr_data { ...@@ -69,6 +108,7 @@ struct trackpoint_attr_data {
unsigned char command; unsigned char command;
unsigned char mask; unsigned char mask;
unsigned char inverted; unsigned char inverted;
unsigned char power_on_default;
}; };
static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf) 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, ...@@ -102,10 +142,11 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
return count; return count;
} }
#define TRACKPOINT_INT_ATTR(_name, _command) \ #define TRACKPOINT_INT_ATTR(_name, _command, _default) \
static struct trackpoint_attr_data trackpoint_attr_##_name = { \ static struct trackpoint_attr_data trackpoint_attr_##_name = { \
.field_offset = offsetof(struct trackpoint_data, _name), \ .field_offset = offsetof(struct trackpoint_data, _name), \
.command = _command, \ .command = _command, \
.power_on_default = _default, \
}; \ }; \
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
&trackpoint_attr_##_name, \ &trackpoint_attr_##_name, \
...@@ -139,31 +180,60 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, ...@@ -139,31 +180,60 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
} }
#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \ #define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \
static struct trackpoint_attr_data trackpoint_attr_##_name = { \ static struct trackpoint_attr_data trackpoint_attr_##_name = { \
.field_offset = offsetof(struct trackpoint_data, _name), \ .field_offset = offsetof(struct trackpoint_data, \
_name), \
.command = _command, \ .command = _command, \
.mask = _mask, \ .mask = _mask, \
.inverted = _inv, \ .inverted = _inv, \
.power_on_default = _default, \
}; \ }; \
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
&trackpoint_attr_##_name, \ &trackpoint_attr_##_name, \
trackpoint_show_int_attr, trackpoint_set_bit_attr) trackpoint_show_int_attr, trackpoint_set_bit_attr)
TRACKPOINT_INT_ATTR(sensitivity, TP_SENS); #define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name) \
TRACKPOINT_INT_ATTR(speed, TP_SPEED); do { \
TRACKPOINT_INT_ATTR(inertia, TP_INERTIA); struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \
TRACKPOINT_INT_ATTR(reach, TP_REACH); \
TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS); trackpoint_update_bit(&_psmouse->ps2dev, \
TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG); _attr->command, _attr->mask, _tp->_name); \
TRACKPOINT_INT_ATTR(thresh, TP_THRESH); } while (0)
TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH);
TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME); #define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \
TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV); do { \
if (!_power_on || \
TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0); _tp->_name != trackpoint_attr_##_name.power_on_default) { \
TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0); if (!trackpoint_attr_##_name.mask) \
TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1); 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[] = { static struct attribute *trackpoint_attrs[] = {
&psmouse_attr_sensitivity.dattr.attr, &psmouse_attr_sensitivity.dattr.attr,
...@@ -202,73 +272,72 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir ...@@ -202,73 +272,72 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir
return 0; 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; 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 */ if (!in_power_on_state) {
trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity); /*
trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia); * Disable features that may make device unusable
trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed); * with this driver.
*/
trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach); trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND,
trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys); TP_MASK_TWOHAND, TP_DEF_TWOHAND);
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);
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle); trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG,
if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select) TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG);
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle); trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB,
if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback) TP_MASK_MB, TP_DEF_MB);
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK); }
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle); /*
if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev) * These properties can be changed in this driver. Only
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV); * 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; return 0;
} }
static void trackpoint_defaults(struct trackpoint_data *tp) static void trackpoint_defaults(struct trackpoint_data *tp)
{ {
tp->press_to_select = TP_DEF_PTSON; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity);
tp->sensitivity = TP_DEF_SENS; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed);
tp->speed = TP_DEF_SPEED; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach);
tp->reach = TP_DEF_REACH; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag);
tp->draghys = TP_DEF_DRAGHYS; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh);
tp->mindrag = TP_DEF_MINDRAG; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime);
tp->thresh = TP_DEF_THRESH; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks);
tp->upthresh = TP_DEF_UP_THRESH; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia);
tp->ztime = TP_DEF_Z_TIME; /* toggles */
tp->jenks = TP_DEF_JENKS_CURV; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback);
tp->inertia = TP_DEF_INERTIA; TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);
tp->skipback = TP_DEF_SKIPBACK;
tp->ext_dev = TP_DEF_EXT_DEV;
} }
static void trackpoint_disconnect(struct psmouse *psmouse) static void trackpoint_disconnect(struct psmouse *psmouse)
...@@ -281,10 +350,13 @@ 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) static int trackpoint_reconnect(struct psmouse *psmouse)
{ {
int reset_fail;
if (trackpoint_start_protocol(psmouse, NULL)) if (trackpoint_start_protocol(psmouse, NULL))
return -1; return -1;
if (trackpoint_sync(psmouse)) reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev);
if (trackpoint_sync(psmouse, !reset_fail))
return -1; return -1;
return 0; return 0;
...@@ -322,7 +394,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) ...@@ -322,7 +394,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
trackpoint_defaults(psmouse->private); 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); error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
if (error) { if (error) {
......
...@@ -126,6 +126,8 @@ ...@@ -126,6 +126,8 @@
#define TP_DEF_PTSON 0x00 #define TP_DEF_PTSON 0x00
#define TP_DEF_SKIPBACK 0x00 #define TP_DEF_SKIPBACK 0x00
#define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */ #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)) #define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd))
...@@ -136,9 +138,9 @@ struct trackpoint_data ...@@ -136,9 +138,9 @@ struct trackpoint_data
unsigned char thresh, upthresh; unsigned char thresh, upthresh;
unsigned char ztime, jenks; unsigned char ztime, jenks;
/* toggles */
unsigned char press_to_select; unsigned char press_to_select;
unsigned char skipback; unsigned char skipback;
unsigned char ext_dev; unsigned char ext_dev;
}; };
......
...@@ -245,4 +245,14 @@ config SERIO_ARC_PS2 ...@@ -245,4 +245,14 @@ config SERIO_ARC_PS2
To compile this driver as a module, choose M here; the module To compile this driver as a module, choose M here; the module
will be called arc_ps2. 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 endif
...@@ -26,3 +26,4 @@ obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o ...@@ -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_XILINX_XPS_PS2) += xilinx_ps2.o
obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
/*
* 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");
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -259,10 +260,19 @@ static int arc_ps2_remove(struct platform_device *pdev) ...@@ -259,10 +260,19 @@ static int arc_ps2_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver arc_ps2_driver = {
.driver = { .driver = {
.name = "arc_ps2", .name = "arc_ps2",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(arc_ps2_match),
}, },
.probe = arc_ps2_probe, .probe = arc_ps2_probe,
.remove = arc_ps2_remove, .remove = arc_ps2_remove,
......
...@@ -359,18 +359,7 @@ static struct platform_driver psif_driver = { ...@@ -359,18 +359,7 @@ static struct platform_driver psif_driver = {
}, },
}; };
static int __init psif_init(void) module_platform_driver_probe(psif_driver, psif_probe);
{
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_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
......
...@@ -193,15 +193,4 @@ static struct platform_driver q40kbd_driver = { ...@@ -193,15 +193,4 @@ static struct platform_driver q40kbd_driver = {
.remove = q40kbd_remove, .remove = q40kbd_remove,
}; };
static int __init q40kbd_init(void) module_platform_driver_probe(q40kbd_driver, q40kbd_probe);
{
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);
...@@ -273,7 +273,7 @@ static int ad7877_write(struct spi_device *spi, u16 reg, u16 val) ...@@ -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) 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; struct ser_req *req;
int status; int status;
int sample; int sample;
...@@ -720,7 +720,7 @@ static int ad7877_probe(struct spi_device *spi) ...@@ -720,7 +720,7 @@ static int ad7877_probe(struct spi_device *spi)
goto err_free_mem; goto err_free_mem;
} }
dev_set_drvdata(&spi->dev, ts); spi_set_drvdata(spi, ts);
ts->spi = spi; ts->spi = spi;
ts->input = input_dev; ts->input = input_dev;
...@@ -806,13 +806,13 @@ static int ad7877_probe(struct spi_device *spi) ...@@ -806,13 +806,13 @@ static int ad7877_probe(struct spi_device *spi)
err_free_mem: err_free_mem:
input_free_device(input_dev); input_free_device(input_dev);
kfree(ts); kfree(ts);
dev_set_drvdata(&spi->dev, NULL); spi_set_drvdata(spi, NULL);
return err; return err;
} }
static int ad7877_remove(struct spi_device *spi) 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); sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
...@@ -823,7 +823,7 @@ static int ad7877_remove(struct spi_device *spi) ...@@ -823,7 +823,7 @@ static int ad7877_remove(struct spi_device *spi)
kfree(ts); kfree(ts);
dev_dbg(&spi->dev, "unregistered touchscreen\n"); dev_dbg(&spi->dev, "unregistered touchscreen\n");
dev_set_drvdata(&spi->dev, NULL); spi_set_drvdata(spi, NULL);
return 0; return 0;
} }
......
...@@ -1245,7 +1245,7 @@ static int ads7846_probe(struct spi_device *spi) ...@@ -1245,7 +1245,7 @@ static int ads7846_probe(struct spi_device *spi)
goto err_free_mem; goto err_free_mem;
} }
dev_set_drvdata(&spi->dev, ts); spi_set_drvdata(spi, ts);
ts->packet = packet; ts->packet = packet;
ts->spi = spi; ts->spi = spi;
...@@ -1397,7 +1397,7 @@ static int ads7846_probe(struct spi_device *spi) ...@@ -1397,7 +1397,7 @@ static int ads7846_probe(struct spi_device *spi)
static int ads7846_remove(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); device_init_wakeup(&spi->dev, false);
......
...@@ -432,17 +432,7 @@ static struct platform_driver atmel_wm97xx_driver = { ...@@ -432,17 +432,7 @@ static struct platform_driver atmel_wm97xx_driver = {
}, },
}; };
static int __init atmel_wm97xx_init(void) module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe);
{
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_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32"); MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/input/auo-pixcir-ts.h> #include <linux/input/auo-pixcir-ts.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
/* /*
* Coordinate calculation: * Coordinate calculation:
...@@ -111,6 +113,7 @@ ...@@ -111,6 +113,7 @@
struct auo_pixcir_ts { struct auo_pixcir_ts {
struct i2c_client *client; struct i2c_client *client;
struct input_dev *input; struct input_dev *input;
const struct auo_pixcir_ts_platdata *pdata;
char phys[32]; char phys[32];
/* special handling for touch_indicate interupt mode */ /* special handling for touch_indicate interupt mode */
...@@ -132,7 +135,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, ...@@ -132,7 +135,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
struct auo_point_t *point) struct auo_point_t *point)
{ {
struct i2c_client *client = ts->client; 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_coord[8];
uint8_t raw_area[4]; uint8_t raw_area[4];
int i, ret; int i, ret;
...@@ -178,8 +181,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, ...@@ -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) static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
{ {
struct auo_pixcir_ts *ts = dev_id; struct auo_pixcir_ts *ts = dev_id;
struct i2c_client *client = ts->client; const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS]; struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
int i; int i;
int ret; int ret;
...@@ -290,7 +292,7 @@ static int auo_pixcir_int_config(struct auo_pixcir_ts *ts, ...@@ -290,7 +292,7 @@ static int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
int int_setting) int int_setting)
{ {
struct i2c_client *client = ts->client; 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; int ret;
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
...@@ -479,53 +481,105 @@ static int auo_pixcir_resume(struct device *dev) ...@@ -479,53 +481,105 @@ static int auo_pixcir_resume(struct device *dev)
} }
#endif #endif
static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops,
auo_pixcir_resume); 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, 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 auo_pixcir_ts *ts;
struct input_dev *input_dev; struct input_dev *input_dev;
int ret; int version;
int error;
if (!pdata)
return -EINVAL; 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) if (!ts)
return -ENOMEM; return -ENOMEM;
ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int"); input_dev = devm_input_allocate_device(&client->dev);
if (ret) { if (!input_dev) {
dev_err(&client->dev, "request of gpio %d failed, %d\n", dev_err(&client->dev, "could not allocate input device\n");
pdata->gpio_int, ret); return -ENOMEM;
goto err_gpio_int;
} }
if (pdata->init_hw) ts->pdata = pdata;
pdata->init_hw(client);
ts->client = client; ts->client = client;
ts->input = input_dev;
ts->touch_ind_mode = 0; ts->touch_ind_mode = 0;
ts->stopped = true;
init_waitqueue_head(&ts->wait); init_waitqueue_head(&ts->wait);
snprintf(ts->phys, sizeof(ts->phys), snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev)); "%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->name = "AUO-Pixcir touchscreen";
input_dev->phys = ts->phys; input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C; input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_dev->open = auo_pixcir_input_open; input_dev->open = auo_pixcir_input_open;
input_dev->close = auo_pixcir_input_close; input_dev->close = auo_pixcir_input_close;
...@@ -550,70 +604,70 @@ static int auo_pixcir_probe(struct i2c_client *client, ...@@ -550,70 +604,70 @@ static int auo_pixcir_probe(struct i2c_client *client,
AUO_PIXCIR_MAX_AREA, 0, 0); AUO_PIXCIR_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 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); input_set_drvdata(ts->input, ts);
ts->stopped = true;
ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt, error = devm_gpio_request_one(&client->dev, pdata->gpio_int,
IRQF_TRIGGER_RISING | IRQF_ONESHOT, GPIOF_DIR_IN, "auo_pixcir_ts_int");
input_dev->name, ts); if (error) {
if (ret) { dev_err(&client->dev, "request of gpio %d failed, %d\n",
dev_err(&client->dev, "irq %d requested failed\n", client->irq); pdata->gpio_int, error);
goto err_fw_vers; return error;
} }
/* stop device and put it into deep sleep until it is opened */ error = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
ret = auo_pixcir_stop(ts); GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
if (ret < 0) "auo_pixcir_ts_rst");
goto err_input_register; if (error) {
dev_err(&client->dev, "request of gpio %d failed, %d\n",
ret = input_register_device(input_dev); pdata->gpio_rst, error);
if (ret) { return error;
dev_err(&client->dev, "could not register input device\n");
goto err_input_register;
} }
i2c_set_clientdata(client, ts); error = devm_add_action(&client->dev, auo_pixcir_reset, ts);
if (error) {
return 0; auo_pixcir_reset(ts);
dev_err(&client->dev, "failed to register reset action, %d\n",
error);
return error;
}
err_input_register: msleep(200);
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);
return ret; version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
} if (version < 0) {
error = version;
static int auo_pixcir_remove(struct i2c_client *client) return error;
{ }
struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
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) error = devm_request_threaded_irq(&client->dev, client->irq,
pdata->exit_hw(client); 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; return 0;
} }
...@@ -624,14 +678,22 @@ static const struct i2c_device_id auo_pixcir_idtable[] = { ...@@ -624,14 +678,22 @@ static const struct i2c_device_id auo_pixcir_idtable[] = {
}; };
MODULE_DEVICE_TABLE(i2c, 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 = { static struct i2c_driver auo_pixcir_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "auo_pixcir_ts", .name = "auo_pixcir_ts",
.pm = &auo_pixcir_pm_ops, .pm = &auo_pixcir_pm_ops,
.of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable),
}, },
.probe = auo_pixcir_probe, .probe = auo_pixcir_probe,
.remove = auo_pixcir_remove,
.id_table = auo_pixcir_idtable, .id_table = auo_pixcir_idtable,
}; };
......
...@@ -440,7 +440,6 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) ...@@ -440,7 +440,6 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
return -EIO; return -EIO;
} }
if (tsdata->raw_buffer)
kfree(tsdata->raw_buffer); kfree(tsdata->raw_buffer);
tsdata->raw_buffer = NULL; tsdata->raw_buffer = NULL;
......
...@@ -206,7 +206,6 @@ static int eeti_ts_probe(struct i2c_client *client, ...@@ -206,7 +206,6 @@ static int eeti_ts_probe(struct i2c_client *client,
if (err < 0) if (err < 0)
goto err1; 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 ? irq_flags = priv->irq_active_high ?
......
...@@ -250,17 +250,7 @@ static struct platform_driver mc13783_ts_driver = { ...@@ -250,17 +250,7 @@ static struct platform_driver mc13783_ts_driver = {
}, },
}; };
static int __init mc13783_ts_init(void) module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe);
{
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_DESCRIPTION("MC13783 input touchscreen driver"); MODULE_DESCRIPTION("MC13783 input touchscreen driver");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
......
...@@ -19,13 +19,16 @@ ...@@ -19,13 +19,16 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/platform_data/st1232_pdata.h>
#define ST1232_TS_NAME "st1232-ts" #define ST1232_TS_NAME "st1232-ts"
...@@ -48,6 +51,7 @@ struct st1232_ts_data { ...@@ -48,6 +51,7 @@ struct st1232_ts_data {
struct input_dev *input_dev; struct input_dev *input_dev;
struct st1232_ts_finger finger[MAX_FINGERS]; struct st1232_ts_finger finger[MAX_FINGERS];
struct dev_pm_qos_request low_latency_req; struct dev_pm_qos_request low_latency_req;
int reset_gpio;
}; };
static int st1232_ts_read_data(struct st1232_ts_data *ts) static int st1232_ts_read_data(struct st1232_ts_data *ts)
...@@ -139,10 +143,17 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) ...@@ -139,10 +143,17 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED; 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, static int st1232_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct st1232_ts_data *ts; struct st1232_ts_data *ts;
struct st1232_pdata *pdata = client->dev.platform_data;
struct input_dev *input_dev; struct input_dev *input_dev;
int error; int error;
...@@ -156,17 +167,36 @@ static int st1232_ts_probe(struct i2c_client *client, ...@@ -156,17 +167,36 @@ static int st1232_ts_probe(struct i2c_client *client,
return -EINVAL; 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 = devm_input_allocate_device(&client->dev);
input_dev = input_allocate_device(); if (!input_dev)
if (!ts || !input_dev) { return -ENOMEM;
error = -ENOMEM;
goto err_free_mem;
}
ts->client = client; ts->client = client;
ts->input_dev = input_dev; 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->name = "st1232-touchscreen";
input_dev->id.bustype = BUS_I2C; input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev; input_dev->dev.parent = &client->dev;
...@@ -179,31 +209,26 @@ static int st1232_ts_probe(struct i2c_client *client, ...@@ -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_X, MIN_X, MAX_X, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 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, error = devm_request_threaded_irq(&client->dev, client->irq,
IRQF_ONESHOT, client->name, ts); NULL, st1232_ts_irq_handler,
IRQF_ONESHOT,
client->name, ts);
if (error) { if (error) {
dev_err(&client->dev, "Failed to register interrupt\n"); dev_err(&client->dev, "Failed to register interrupt\n");
goto err_free_mem; return error;
} }
error = input_register_device(ts->input_dev); error = input_register_device(ts->input_dev);
if (error) { if (error) {
dev_err(&client->dev, "Unable to register %s input device\n", dev_err(&client->dev, "Unable to register %s input device\n",
input_dev->name); input_dev->name);
goto err_free_irq; return error;
} }
i2c_set_clientdata(client, ts); i2c_set_clientdata(client, ts);
device_init_wakeup(&client->dev, 1); device_init_wakeup(&client->dev, 1);
return 0; 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) static int st1232_ts_remove(struct i2c_client *client)
...@@ -211,9 +236,7 @@ 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); struct st1232_ts_data *ts = i2c_get_clientdata(client);
device_init_wakeup(&client->dev, 0); device_init_wakeup(&client->dev, 0);
free_irq(client->irq, ts); st1232_ts_power(ts, false);
input_unregister_device(ts->input_dev);
kfree(ts);
return 0; return 0;
} }
...@@ -222,11 +245,14 @@ static int st1232_ts_remove(struct i2c_client *client) ...@@ -222,11 +245,14 @@ static int st1232_ts_remove(struct i2c_client *client)
static int st1232_ts_suspend(struct device *dev) static int st1232_ts_suspend(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(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); enable_irq_wake(client->irq);
else } else {
disable_irq(client->irq); disable_irq(client->irq);
st1232_ts_power(ts, false);
}
return 0; return 0;
} }
...@@ -234,11 +260,14 @@ static int st1232_ts_suspend(struct device *dev) ...@@ -234,11 +260,14 @@ static int st1232_ts_suspend(struct device *dev)
static int st1232_ts_resume(struct device *dev) static int st1232_ts_resume(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(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); disable_irq_wake(client->irq);
else } else {
st1232_ts_power(ts, true);
enable_irq(client->irq); enable_irq(client->irq);
}
return 0; return 0;
} }
......
...@@ -162,14 +162,14 @@ static void wm9712_phy_init(struct wm97xx *wm) ...@@ -162,14 +162,14 @@ static void wm9712_phy_init(struct wm97xx *wm)
if (rpu) { if (rpu) {
dig2 &= 0xffc0; dig2 &= 0xffc0;
dig2 |= WM9712_RPU(rpu); 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); 64000 / rpu);
} }
/* WM9712 five wire */ /* WM9712 five wire */
if (five_wire) { if (five_wire) {
dig2 |= WM9712_45W; dig2 |= WM9712_45W;
dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n");
if (pil) { if (pil) {
dev_warn(wm->dev, "pressure measurement is not " dev_warn(wm->dev, "pressure measurement is not "
...@@ -182,21 +182,21 @@ static void wm9712_phy_init(struct wm97xx *wm) ...@@ -182,21 +182,21 @@ static void wm9712_phy_init(struct wm97xx *wm)
if (pil == 2) { if (pil == 2) {
dig2 |= WM9712_PIL; dig2 |= WM9712_PIL;
dev_dbg(wm->dev, dev_dbg(wm->dev,
"setting pressure measurement current to 400uA."); "setting pressure measurement current to 400uA.\n");
} else if (pil) } else if (pil)
dev_dbg(wm->dev, dev_dbg(wm->dev,
"setting pressure measurement current to 200uA."); "setting pressure measurement current to 200uA.\n");
if (!pil) if (!pil)
pressure = 0; pressure = 0;
/* polling mode sample settling delay */ /* polling mode sample settling delay */
if (delay < 0 || delay > 15) { 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; delay = 4;
} }
dig1 &= 0xff0f; dig1 &= 0xff0f;
dig1 |= WM97XX_DELAY(delay); 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]); delay_table[delay]);
/* mask */ /* mask */
...@@ -285,7 +285,7 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) ...@@ -285,7 +285,7 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
if (is_pden(wm)) if (is_pden(wm))
wm->pen_probably_down = 0; wm->pen_probably_down = 0;
else else
dev_dbg(wm->dev, "adc sample timeout"); dev_dbg(wm->dev, "adc sample timeout\n");
return RC_PENUP; return RC_PENUP;
} }
...@@ -295,16 +295,20 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) ...@@ -295,16 +295,20 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
/* check we have correct sample */ /* check we have correct sample */
if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { 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, adcsel & WM97XX_ADCSEL_MASK,
*sample & WM97XX_ADCSEL_MASK); *sample & WM97XX_ADCSEL_MASK);
return RC_PENUP; return RC_AGAIN;
} }
if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
/* 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; wm->pen_probably_down = 0;
return RC_PENUP; return RC_PENUP;
} }
}
return RC_VALID; return RC_VALID;
} }
...@@ -345,7 +349,7 @@ static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) ...@@ -345,7 +349,7 @@ static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
if (is_pden(wm)) if (is_pden(wm))
wm->pen_probably_down = 0; wm->pen_probably_down = 0;
else else
dev_dbg(wm->dev, "adc sample timeout"); dev_dbg(wm->dev, "adc sample timeout\n");
return RC_PENUP; return RC_PENUP;
} }
......
...@@ -442,6 +442,16 @@ static int wm97xx_read_samples(struct wm97xx *wm) ...@@ -442,6 +442,16 @@ static int wm97xx_read_samples(struct wm97xx *wm)
"pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
data.x >> 12, data.x & 0xfff, data.y >> 12, data.x >> 12, data.x & 0xfff, data.y >> 12,
data.y & 0xfff, data.p >> 12, data.p & 0xfff); 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_X, data.x & 0xfff);
input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
...@@ -455,6 +465,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) ...@@ -455,6 +465,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
wm->ts_reader_interval = wm->ts_reader_min_interval; wm->ts_reader_interval = wm->ts_reader_min_interval;
} }
out:
mutex_unlock(&wm->codec_mutex); mutex_unlock(&wm->codec_mutex);
return rc; return rc;
} }
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/jiffies.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
...@@ -51,6 +52,9 @@ ...@@ -51,6 +52,9 @@
static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
static bool __read_mostly sysrq_always_enabled; static bool __read_mostly sysrq_always_enabled;
unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
int sysrq_reset_downtime_ms __weak;
static bool sysrq_on(void) static bool sysrq_on(void)
{ {
return sysrq_enabled || sysrq_always_enabled; return sysrq_enabled || sysrq_always_enabled;
...@@ -586,6 +590,7 @@ struct sysrq_state { ...@@ -586,6 +590,7 @@ struct sysrq_state {
int reset_seq_len; int reset_seq_len;
int reset_seq_cnt; int reset_seq_cnt;
int reset_seq_version; int reset_seq_version;
struct timer_list keyreset_timer;
}; };
#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
...@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) ...@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
state->reset_seq_version = sysrq_reset_seq_version; state->reset_seq_version = sysrq_reset_seq_version;
} }
static bool sysrq_detect_reset_sequence(struct sysrq_state *state, static void sysrq_do_reset(unsigned long dummy)
{
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
static void sysrq_handle_reset_request(struct sysrq_state *state)
{
if (sysrq_reset_downtime_ms)
mod_timer(&state->keyreset_timer,
jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms));
else
sysrq_do_reset(0);
}
static void sysrq_detect_reset_sequence(struct sysrq_state *state,
unsigned int code, int value) unsigned int code, int value)
{ {
if (!test_bit(code, state->reset_keybit)) { if (!test_bit(code, state->reset_keybit)) {
/* /*
* Pressing any key _not_ in reset sequence cancels * Pressing any key _not_ in reset sequence cancels
* the reset sequence. * the reset sequence. Also cancelling the timer in
* case additional keys were pressed after a reset
* has been requested.
*/ */
if (value && state->reset_seq_cnt) if (value && state->reset_seq_cnt) {
state->reset_canceled = true; state->reset_canceled = true;
del_timer(&state->keyreset_timer);
}
} else if (value == 0) { } else if (value == 0) {
/* key release */ /*
* Key release - all keys in the reset sequence need
* to be pressed and held for the reset timeout
* to hold.
*/
del_timer(&state->keyreset_timer);
if (--state->reset_seq_cnt == 0) if (--state->reset_seq_cnt == 0)
state->reset_canceled = false; state->reset_canceled = false;
} else if (value == 1) { } else if (value == 1) {
/* key press, not autorepeat */ /* key press, not autorepeat */
if (++state->reset_seq_cnt == state->reset_seq_len && if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) { !state->reset_canceled) {
return true; sysrq_handle_reset_request(state);
} }
} }
return false;
} }
static void sysrq_reinject_alt_sysrq(struct work_struct *work) static void sysrq_reinject_alt_sysrq(struct work_struct *work)
...@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq, ...@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
if (was_active) if (was_active)
schedule_work(&sysrq->reinject_work); schedule_work(&sysrq->reinject_work);
if (sysrq_detect_reset_sequence(sysrq, code, value)) { /* Check for reset sequence */
/* Force emergency reboot */ sysrq_detect_reset_sequence(sysrq, code, value);
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
/* /*
...@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler, ...@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler,
sysrq->handle.handler = handler; sysrq->handle.handler = handler;
sysrq->handle.name = "sysrq"; sysrq->handle.name = "sysrq";
sysrq->handle.private = sysrq; sysrq->handle.private = sysrq;
setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0);
error = input_register_handle(&sysrq->handle); error = input_register_handle(&sysrq->handle);
if (error) { if (error) {
...@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle) ...@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle)
input_close_device(handle); input_close_device(handle);
cancel_work_sync(&sysrq->reinject_work); cancel_work_sync(&sysrq->reinject_work);
del_timer_sync(&sysrq->keyreset_timer);
input_unregister_handle(handle); input_unregister_handle(handle);
kfree(sysrq); kfree(sysrq);
} }
...@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = { ...@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = {
static bool sysrq_handler_registered; static bool sysrq_handler_registered;
unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)
{ {
unsigned short key; unsigned short key;
...@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = { ...@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = {
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
&sysrq_reset_seq_len, 0644); &sysrq_reset_seq_len, 0644);
module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);
#else #else
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)
......
...@@ -962,6 +962,10 @@ static int acm_probe(struct usb_interface *intf, ...@@ -962,6 +962,10 @@ static int acm_probe(struct usb_interface *intf,
/* normal quirks */ /* normal quirks */
quirks = (unsigned long)id->driver_info; quirks = (unsigned long)id->driver_info;
if (quirks == IGNORE_DEVICE)
return -ENODEV;
num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
/* handle quirks deadly to normal probing*/ /* handle quirks deadly to normal probing*/
...@@ -1675,6 +1679,15 @@ static const struct usb_device_id acm_ids[] = { ...@@ -1675,6 +1679,15 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = NO_DATA_INTERFACE, .driver_info = NO_DATA_INTERFACE,
}, },
#if IS_ENABLED(CONFIG_INPUT_IMS_PCU)
{ USB_DEVICE(0x04d8, 0x0082), /* Application mode */
.driver_info = IGNORE_DEVICE,
},
{ USB_DEVICE(0x04d8, 0x0083), /* Bootloader mode */
.driver_info = IGNORE_DEVICE,
},
#endif
/* control interfaces without any protocol set */ /* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) }, USB_CDC_PROTO_NONE) },
......
...@@ -128,3 +128,4 @@ struct acm { ...@@ -128,3 +128,4 @@ struct acm {
#define NO_CAP_LINE 4 #define NO_CAP_LINE 4
#define NOT_A_MODEM 8 #define NOT_A_MODEM 8
#define NO_DATA_INTERFACE 16 #define NO_DATA_INTERFACE 16
#define IGNORE_DEVICE 32
...@@ -576,6 +576,10 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res); ...@@ -576,6 +576,10 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
void __iomem *devm_request_and_ioremap(struct device *dev, void __iomem *devm_request_and_ioremap(struct device *dev,
struct resource *res); struct resource *res);
/* allows to add/remove a custom action to devres stack */
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
struct device_dma_parameters { struct device_dma_parameters {
/* /*
* a low level driver may set these to teach IOMMU code about * a low level driver may set these to teach IOMMU code about
......
...@@ -43,12 +43,10 @@ ...@@ -43,12 +43,10 @@
*/ */
struct auo_pixcir_ts_platdata { struct auo_pixcir_ts_platdata {
int gpio_int; int gpio_int;
int gpio_rst;
int int_setting; int int_setting;
void (*init_hw)(struct i2c_client *);
void (*exit_hw)(struct i2c_client *);
unsigned int x_max; unsigned int x_max;
unsigned int y_max; unsigned int y_max;
}; };
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */ #define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */ #define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */ #define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
#define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */
/** /**
* struct input_mt_slot - represents the state of an input MT slot * struct input_mt_slot - represents the state of an input MT slot
......
#ifndef _LINUX_ST1232_PDATA_H
#define _LINUX_ST1232_PDATA_H
/*
* Optional platform data
*
* Use this if you want the driver to drive the reset pin.
*/
struct st1232_pdata {
int reset_gpio;
};
#endif
...@@ -702,6 +702,11 @@ struct input_keymap_entry { ...@@ -702,6 +702,11 @@ struct input_keymap_entry {
#define KEY_CAMERA_LEFT 0x219 #define KEY_CAMERA_LEFT 0x219
#define KEY_CAMERA_RIGHT 0x21a #define KEY_CAMERA_RIGHT 0x21a
#define KEY_ATTENDANT_ON 0x21b
#define KEY_ATTENDANT_OFF 0x21c
#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */
#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */
#define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1 #define BTN_TRIGGER_HAPPY2 0x2c1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment