diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2020-01-31 17:42:33 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2020-01-31 17:42:33 -0800 |
commit | b19efcabb587e5470a423ef778905f47e5a47f1a (patch) | |
tree | 8863c2233ed8a30d55c4e4029a98c3d7faf359a8 /drivers/input | |
parent | 996d5d5f89a558a3608a46e73ccd1b99f1b1d058 (diff) | |
parent | c5ccf2ad3d33413fee06ae87d0b970d8cc540db6 (diff) | |
download | linux-b19efcabb587e5470a423ef778905f47e5a47f1a.tar.gz linux-b19efcabb587e5470a423ef778905f47e5a47f1a.tar.bz2 linux-b19efcabb587e5470a423ef778905f47e5a47f1a.zip |
Merge branch 'next' into for-linus
Prepare input updates for 5.6 merge window.
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/cros_ec_keyb.c | 6 | ||||
-rw-r--r-- | drivers/input/misc/axp20x-pek.c | 45 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f11.c | 14 | ||||
-rw-r--r-- | drivers/input/serio/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/serio/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/serio/apbps2.c | 2 | ||||
-rw-r--r-- | drivers/input/serio/ioc3kbd.c | 216 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 15 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 55 | ||||
-rw-r--r-- | drivers/input/touchscreen/elants_i2c.c | 77 |
10 files changed, 382 insertions, 59 deletions
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 8d4d9786cc74..2b71c5a51f90 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -226,8 +226,6 @@ static int cros_ec_keyb_work(struct notifier_block *nb, { struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, notifier); - uint8_t mkbp_event_type = ckdev->ec->event_data.event_type & - EC_MKBP_EVENT_TYPE_MASK; u32 val; unsigned int ev_type; @@ -239,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb, if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) return NOTIFY_OK; - switch (mkbp_event_type) { + switch (ckdev->ec->event_data.event_type) { case EC_MKBP_EVENT_KEY_MATRIX: pm_wakeup_event(ckdev->dev, 0); @@ -266,7 +264,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb, case EC_MKBP_EVENT_SWITCH: pm_wakeup_event(ckdev->dev, 0); - if (mkbp_event_type == EC_MKBP_EVENT_BUTTON) { + if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { val = get_unaligned_le32( &ckdev->ec->event_data.data.buttons); ev_type = EV_KEY; diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index 17c1cca74498..c8f87df93a50 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -191,9 +191,10 @@ static ssize_t axp20x_store_attr_shutdown(struct device *dev, axp20x_pek->info->shutdown_mask, buf, count); } -DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, axp20x_store_attr_startup); -DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown, - axp20x_store_attr_shutdown); +static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, + axp20x_store_attr_startup); +static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown, + axp20x_store_attr_shutdown); static struct attribute *axp20x_attrs[] = { &dev_attr_startup.attr, @@ -279,8 +280,7 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, return error; } - if (axp20x_pek->axp20x->variant == AXP288_ID) - enable_irq_wake(axp20x_pek->irq_dbr); + device_init_wakeup(&pdev->dev, true); return 0; } @@ -352,6 +352,40 @@ static int axp20x_pek_probe(struct platform_device *pdev) return 0; } +static int __maybe_unused axp20x_pek_suspend(struct device *dev) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + + /* + * As nested threaded IRQs are not automatically disabled during + * suspend, we must explicitly disable non-wakeup IRQs. + */ + if (device_may_wakeup(dev)) { + enable_irq_wake(axp20x_pek->irq_dbf); + enable_irq_wake(axp20x_pek->irq_dbr); + } else { + disable_irq(axp20x_pek->irq_dbf); + disable_irq(axp20x_pek->irq_dbr); + } + + return 0; +} + +static int __maybe_unused axp20x_pek_resume(struct device *dev) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(axp20x_pek->irq_dbf); + disable_irq_wake(axp20x_pek->irq_dbr); + } else { + enable_irq(axp20x_pek->irq_dbf); + enable_irq(axp20x_pek->irq_dbr); + } + + return 0; +} + static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev) { struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); @@ -371,6 +405,7 @@ static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev) } static const struct dev_pm_ops axp20x_pek_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume) #ifdef CONFIG_PM_SLEEP .resume_noirq = axp20x_pek_resume_noirq, #endif diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index bbf9ae9f3f0c..6adea8a3e8fb 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -412,6 +412,10 @@ struct f11_2d_sensor_queries { /* Defs for Ctrl0. */ #define RMI_F11_REPORT_MODE_MASK 0x07 +#define RMI_F11_REPORT_MODE_CONTINUOUS (0 << 0) +#define RMI_F11_REPORT_MODE_REDUCED (1 << 0) +#define RMI_F11_REPORT_MODE_FS_CHANGE (2 << 0) +#define RMI_F11_REPORT_MODE_FP_CHANGE (3 << 0) #define RMI_F11_ABS_POS_FILT (1 << 3) #define RMI_F11_REL_POS_FILT (1 << 4) #define RMI_F11_REL_BALLISTICS (1 << 5) @@ -1195,6 +1199,16 @@ static int rmi_f11_initialize(struct rmi_function *fn) ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = sensor->axis_align.delta_y_threshold; + /* + * If distance threshold values are set, switch to reduced reporting + * mode so they actually get used by the controller. + */ + if (ctrl->ctrl0_11[RMI_F11_DELTA_X_THRESHOLD] || + ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD]) { + ctrl->ctrl0_11[0] &= ~RMI_F11_REPORT_MODE_MASK; + ctrl->ctrl0_11[0] |= RMI_F11_REPORT_MODE_REDUCED; + } + if (f11->sens_query.has_dribble) { switch (sensor->dribble) { case RMI_REG_STATE_OFF: diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index f3e18f8ef9ca..373a1646019e 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -165,6 +165,16 @@ config SERIO_MACEPS2 To compile this driver as a module, choose M here: the module will be called maceps2. +config SERIO_SGI_IOC3 + tristate "SGI IOC3 PS/2 controller" + depends on SGI_MFD_IOC3 + help + Say Y here if you have an SGI Onyx2, SGI Octane or IOC3 PCI card + and you want to attach and use a keyboard, mouse, or both. + + To compile this driver as a module, choose M here: the + module will be called ioc3kbd. + config SERIO_LIBPS2 tristate "PS/2 driver library" depends on SERIO_I8042 || SERIO_I8042=n diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 67950a5ccb3f..6d97bad7b844 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o +obj-$(CONFIG_SERIO_SGI_IOC3) += ioc3kbd.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c index f290d5d146c3..594ac4e6f8ea 100644 --- a/drivers/input/serio/apbps2.c +++ b/drivers/input/serio/apbps2.c @@ -51,7 +51,7 @@ struct apbps2_regs { struct apbps2_priv { struct serio *io; - struct apbps2_regs *regs; + struct apbps2_regs __iomem *regs; }; static int apbps2_idx; diff --git a/drivers/input/serio/ioc3kbd.c b/drivers/input/serio/ioc3kbd.c new file mode 100644 index 000000000000..d51bfe912db5 --- /dev/null +++ b/drivers/input/serio/ioc3kbd.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SGI IOC3 PS/2 controller driver for linux + * + * Copyright (C) 2019 Thomas Bogendoerfer <tbogendoerfer@suse.de> + * + * Based on code Copyright (C) 2005 Stanislaw Skowronek <skylark@unaligned.org> + * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de> + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/serio.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/sn/ioc3.h> + +struct ioc3kbd_data { + struct ioc3_serioregs __iomem *regs; + struct serio *kbd, *aux; + bool kbd_exists, aux_exists; + int irq; +}; + +static int ioc3kbd_wait(struct ioc3_serioregs __iomem *regs, u32 mask) +{ + unsigned long timeout = 0; + + while ((readl(®s->km_csr) & mask) && (timeout < 250)) { + udelay(50); + timeout++; + } + return (timeout >= 250) ? -ETIMEDOUT : 0; +} + +static int ioc3kbd_write(struct serio *dev, u8 val) +{ + struct ioc3kbd_data *d = dev->port_data; + int ret; + + ret = ioc3kbd_wait(d->regs, KM_CSR_K_WRT_PEND); + if (ret) + return ret; + + writel(val, &d->regs->k_wd); + + return 0; +} + +static int ioc3kbd_start(struct serio *dev) +{ + struct ioc3kbd_data *d = dev->port_data; + + d->kbd_exists = true; + return 0; +} + +static void ioc3kbd_stop(struct serio *dev) +{ + struct ioc3kbd_data *d = dev->port_data; + + d->kbd_exists = false; +} + +static int ioc3aux_write(struct serio *dev, u8 val) +{ + struct ioc3kbd_data *d = dev->port_data; + int ret; + + ret = ioc3kbd_wait(d->regs, KM_CSR_M_WRT_PEND); + if (ret) + return ret; + + writel(val, &d->regs->m_wd); + + return 0; +} + +static int ioc3aux_start(struct serio *dev) +{ + struct ioc3kbd_data *d = dev->port_data; + + d->aux_exists = true; + return 0; +} + +static void ioc3aux_stop(struct serio *dev) +{ + struct ioc3kbd_data *d = dev->port_data; + + d->aux_exists = false; +} + +static void ioc3kbd_process_data(struct serio *dev, u32 data) +{ + if (data & KM_RD_VALID_0) + serio_interrupt(dev, (data >> KM_RD_DATA_0_SHIFT) & 0xff, 0); + if (data & KM_RD_VALID_1) + serio_interrupt(dev, (data >> KM_RD_DATA_1_SHIFT) & 0xff, 0); + if (data & KM_RD_VALID_2) + serio_interrupt(dev, (data >> KM_RD_DATA_2_SHIFT) & 0xff, 0); +} + +static irqreturn_t ioc3kbd_intr(int itq, void *dev_id) +{ + struct ioc3kbd_data *d = dev_id; + u32 data_k, data_m; + + data_k = readl(&d->regs->k_rd); + if (d->kbd_exists) + ioc3kbd_process_data(d->kbd, data_k); + + data_m = readl(&d->regs->m_rd); + if (d->aux_exists) + ioc3kbd_process_data(d->aux, data_m); + + return IRQ_HANDLED; +} + +static int ioc3kbd_probe(struct platform_device *pdev) +{ + struct ioc3_serioregs __iomem *regs; + struct device *dev = &pdev->dev; + struct ioc3kbd_data *d; + struct serio *sk, *sa; + int irq, ret; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENXIO; + + d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + sk = kzalloc(sizeof(*sk), GFP_KERNEL); + if (!sk) + return -ENOMEM; + + sa = kzalloc(sizeof(*sa), GFP_KERNEL); + if (!sa) { + kfree(sk); + return -ENOMEM; + } + + sk->id.type = SERIO_8042; + sk->write = ioc3kbd_write; + sk->start = ioc3kbd_start; + sk->stop = ioc3kbd_stop; + snprintf(sk->name, sizeof(sk->name), "IOC3 keyboard %d", pdev->id); + snprintf(sk->phys, sizeof(sk->phys), "ioc3/serio%dkbd", pdev->id); + sk->port_data = d; + sk->dev.parent = dev; + + sa->id.type = SERIO_8042; + sa->write = ioc3aux_write; + sa->start = ioc3aux_start; + sa->stop = ioc3aux_stop; + snprintf(sa->name, sizeof(sa->name), "IOC3 auxiliary %d", pdev->id); + snprintf(sa->phys, sizeof(sa->phys), "ioc3/serio%daux", pdev->id); + sa->port_data = d; + sa->dev.parent = dev; + + d->regs = regs; + d->kbd = sk; + d->aux = sa; + d->irq = irq; + + platform_set_drvdata(pdev, d); + serio_register_port(d->kbd); + serio_register_port(d->aux); + + ret = request_irq(irq, ioc3kbd_intr, IRQF_SHARED, "ioc3-kbd", d); + if (ret) { + dev_err(dev, "could not request IRQ %d\n", irq); + serio_unregister_port(d->kbd); + serio_unregister_port(d->aux); + return ret; + } + + /* enable ports */ + writel(KM_CSR_K_CLAMP_3 | KM_CSR_M_CLAMP_3, ®s->km_csr); + + return 0; +} + +static int ioc3kbd_remove(struct platform_device *pdev) +{ + struct ioc3kbd_data *d = platform_get_drvdata(pdev); + + free_irq(d->irq, d); + + serio_unregister_port(d->kbd); + serio_unregister_port(d->aux); + + return 0; +} + +static struct platform_driver ioc3kbd_driver = { + .probe = ioc3kbd_probe, + .remove = ioc3kbd_remove, + .driver = { + .name = "ioc3-kbd", + }, +}; +module_platform_driver(ioc3kbd_driver); + +MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfer@suse.de>"); +MODULE_DESCRIPTION("SGI IOC3 serio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 51ddb204ca1b..8fd7fc39c4fd 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -333,7 +333,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) req->xfer[1].len = 2; /* for 1uF, settle for 800 usec; no cap, 100 usec. */ - req->xfer[1].delay_usecs = ts->vref_delay_usecs; + req->xfer[1].delay.value = ts->vref_delay_usecs; + req->xfer[1].delay.unit = SPI_DELAY_UNIT_USECS; spi_message_add_tail(&req->xfer[1], &req->msg); /* Enable reference voltage */ @@ -1018,7 +1019,8 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, * have had enough time to stabilize. */ if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + x->delay.value = pdata->settle_delay_usecs; + x->delay.unit = SPI_DELAY_UNIT_USECS; x++; x->tx_buf = &packet->read_y; @@ -1061,7 +1063,8 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, /* ... maybe discard first sample ... */ if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + x->delay.value = pdata->settle_delay_usecs; + x->delay.unit = SPI_DELAY_UNIT_USECS; x++; x->tx_buf = &packet->read_x; @@ -1094,7 +1097,8 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, /* ... maybe discard first sample ... */ if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + x->delay.value = pdata->settle_delay_usecs; + x->delay.unit = SPI_DELAY_UNIT_USECS; x++; x->tx_buf = &packet->read_z1; @@ -1125,7 +1129,8 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, /* ... maybe discard first sample ... */ if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + x->delay.value = pdata->settle_delay_usecs; + x->delay.unit = SPI_DELAY_UNIT_USECS; x++; x->tx_buf = &packet->read_z2; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d61731c0037d..d2587724c52a 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -13,22 +13,23 @@ * http://www.glyn.com/Products/Displays */ -#include <linux/module.h> -#include <linux/ratelimit.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/input.h> -#include <linux/i2c.h> -#include <linux/kernel.h> -#include <linux/uaccess.h> -#include <linux/delay.h> #include <linux/debugfs.h> -#include <linux/slab.h> +#include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -#include <asm/unaligned.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ratelimit.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <asm/unaligned.h> #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -1050,6 +1051,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, { const struct edt_i2c_chip_data *chip_data; struct edt_ft5x06_ts_data *tsdata; + u8 buf[2] = { 0xfc, 0x00 }; struct input_dev *input; unsigned long irq_flags; int error; @@ -1140,6 +1142,12 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } + /* + * Dummy read access. EP0700MLP1 returns bogus data on the first + * register read access and ignores writes. + */ + edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf); + edt_ft5x06_ts_set_regs(tsdata); edt_ft5x06_ts_get_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); @@ -1200,7 +1208,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); - device_init_wakeup(&client->dev, 1); dev_dbg(&client->dev, "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", @@ -1220,29 +1227,6 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client) return 0; } -static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(dev)) - enable_irq_wake(client->irq); - - return 0; -} - -static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(dev)) - disable_irq_wake(client->irq); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, - edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); - static const struct edt_i2c_chip_data edt_ft5x06_data = { .max_support_points = 5, }; @@ -1281,7 +1265,6 @@ static struct i2c_driver edt_ft5x06_ts_driver = { .driver = { .name = "edt_ft5x06", .of_match_table = edt_ft5x06_of_match, - .pm = &edt_ft5x06_ts_pm_ops, }, .id_table = edt_ft5x06_ts_id, .probe = edt_ft5x06_ts_probe, diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index d4ad24ea54c8..491179967b29 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -59,8 +59,10 @@ #define CMD_HEADER_WRITE 0x54 #define CMD_HEADER_READ 0x53 #define CMD_HEADER_6B_READ 0x5B +#define CMD_HEADER_ROM_READ 0x96 #define CMD_HEADER_RESP 0x52 #define CMD_HEADER_6B_RESP 0x9B +#define CMD_HEADER_ROM_RESP 0x95 #define CMD_HEADER_HELLO 0x55 #define CMD_HEADER_REK 0x66 @@ -200,6 +202,10 @@ static int elants_i2c_execute_command(struct i2c_client *client, expected_response = CMD_HEADER_6B_RESP; break; + case CMD_HEADER_ROM_READ: + expected_response = CMD_HEADER_ROM_RESP; + break; + default: dev_err(&client->dev, "%s: invalid command %*ph\n", __func__, (int)cmd_size, cmd); @@ -556,6 +562,8 @@ static int elants_i2c_initialize(struct elants_data *ts) /* hw version is available even if device in recovery state */ error2 = elants_i2c_query_hw_version(ts); + if (!error2) + error2 = elants_i2c_query_bc_version(ts); if (!error) error = error2; @@ -564,8 +572,6 @@ static int elants_i2c_initialize(struct elants_data *ts) if (!error) error = elants_i2c_query_test_version(ts); if (!error) - error = elants_i2c_query_bc_version(ts); - if (!error) error = elants_i2c_query_ts_info(ts); if (error) @@ -613,39 +619,94 @@ static int elants_i2c_fw_write_page(struct i2c_client *client, return error; } +static int elants_i2c_validate_remark_id(struct elants_data *ts, + const struct firmware *fw) +{ + struct i2c_client *client = ts->client; + int error; + const u8 cmd[] = { CMD_HEADER_ROM_READ, 0x80, 0x1F, 0x00, 0x00, 0x21 }; + u8 resp[6] = { 0 }; + u16 ts_remark_id = 0; + u16 fw_remark_id = 0; + + /* Compare TS Remark ID and FW Remark ID */ + error = elants_i2c_execute_command(client, cmd, sizeof(cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, "failed to query Remark ID: %d\n", error); + return error; + } + + ts_remark_id = get_unaligned_be16(&resp[3]); + + fw_remark_id = get_unaligned_le16(&fw->data[fw->size - 4]); + + if (fw_remark_id != ts_remark_id) { + dev_err(&client->dev, + "Remark ID Mismatched: ts_remark_id=0x%04x, fw_remark_id=0x%04x.\n", + ts_remark_id, fw_remark_id); + return -EINVAL; + } + + return 0; +} + static int elants_i2c_do_update_firmware(struct i2c_client *client, const struct firmware *fw, bool force) { + struct elants_data *ts = i2c_get_clientdata(client); const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 }; const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 }; const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc }; - const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01}; + const u8 close_idle[] = { 0x54, 0x2c, 0x01, 0x01 }; u8 buf[HEADER_SIZE]; u16 send_id; int page, n_fw_pages; int error; + bool check_remark_id = ts->iap_version >= 0x60; /* Recovery mode detection! */ if (force) { dev_dbg(&client->dev, "Recovery mode procedure\n"); + + if (check_remark_id) { + error = elants_i2c_validate_remark_id(ts, fw); + if (error) + return error; + } + error = elants_i2c_send(client, enter_iap2, sizeof(enter_iap2)); + if (error) { + dev_err(&client->dev, "failed to enter IAP mode: %d\n", + error); + return error; + } } else { /* Start IAP Procedure */ dev_dbg(&client->dev, "Normal IAP procedure\n"); + /* Close idle mode */ error = elants_i2c_send(client, close_idle, sizeof(close_idle)); if (error) dev_err(&client->dev, "Failed close idle: %d\n", error); msleep(60); + elants_i2c_sw_reset(client); msleep(20); - error = elants_i2c_send(client, enter_iap, sizeof(enter_iap)); - } - if (error) { - dev_err(&client->dev, "failed to enter IAP mode: %d\n", error); - return error; + if (check_remark_id) { + error = elants_i2c_validate_remark_id(ts, fw); + if (error) + return error; + } + + error = elants_i2c_send(client, enter_iap, sizeof(enter_iap)); + if (error) { + dev_err(&client->dev, "failed to enter IAP mode: %d\n", + error); + return error; + } } msleep(20); |