From 0f78ba96bbcf30a78224fe56f8fd72f87915afdd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2016 15:32:14 -0800 Subject: Input: gpio_keys_polled - keep button data constant Commit 633a21d80b4a ("input: gpio_keys_polled: Add support for GPIO descriptors") placed gpio descriptor into gpio_keys_button structure, which is supposed to be part of platform data and not modifiable by the driver. To keep the data constant, let's move the descriptor to gpio_keys_button_data structure instead. Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- include/linux/gpio_keys.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ee2d8c6f9130..0b71024c082c 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -2,7 +2,6 @@ #define _GPIO_KEYS_H struct device; -struct gpio_desc; /** * struct gpio_keys_button - configuration parameters @@ -18,7 +17,6 @@ struct gpio_desc; * disable button via sysfs * @value: axis value for %EV_ABS * @irq: Irq number in case of interrupt keys - * @gpiod: GPIO descriptor */ struct gpio_keys_button { unsigned int code; @@ -31,7 +29,6 @@ struct gpio_keys_button { bool can_disable; int value; unsigned int irq; - struct gpio_desc *gpiod; }; /** @@ -46,7 +43,7 @@ struct gpio_keys_button { * @name: input device name */ struct gpio_keys_platform_data { - struct gpio_keys_button *buttons; + const struct gpio_keys_button *buttons; int nbuttons; unsigned int poll_interval; unsigned int rep:1; -- cgit v1.2.3 From 3aeed5b573f97b4525841cc07c1e948227af389f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 8 Nov 2016 16:34:57 -0800 Subject: Input: synaptics-rmi4 - move IRQ handling to rmi_driver The attn IRQ is related to the chip, rather than the transport, so move all handling of interrupts to the core driver. This also makes sure that there are no races between interrupts and availability of the resources used by the core driver. Signed-off-by: Bjorn Andersson Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 73 +++++++++++++++++++++++++++++++++++++--- drivers/input/rmi4/rmi_i2c.c | 74 +++-------------------------------------- drivers/input/rmi4/rmi_spi.c | 72 +++------------------------------------ include/linux/rmi.h | 7 ++-- 4 files changed, 83 insertions(+), 143 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 06feede3ce17..4f8d19794b01 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ static void process_one_interrupt(struct rmi_driver_data *data, } } -int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) +static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct device *dev = &rmi_dev->dev; @@ -181,7 +182,42 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) return 0; } -EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests); + +static irqreturn_t rmi_irq_fn(int irq, void *dev_id) +{ + struct rmi_device *rmi_dev = dev_id; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_irq_init(struct rmi_device *rmi_dev) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq_flags = irq_get_trigger_type(pdata->irq); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&rmi_dev->dev, pdata->irq, NULL, + rmi_irq_fn, irq_flags | IRQF_ONESHOT, + dev_name(rmi_dev->xport->dev), + rmi_dev); + if (ret < 0) { + dev_err(&rmi_dev->dev, "Failed to register interrupt %d\n", + pdata->irq); + + return ret; + } + + return 0; +} static int suspend_one_function(struct rmi_function *fn) { @@ -802,8 +838,10 @@ err_put_fn: return error; } -int rmi_driver_suspend(struct rmi_device *rmi_dev) +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) { + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; int retval = 0; retval = rmi_suspend_functions(rmi_dev); @@ -811,14 +849,33 @@ int rmi_driver_suspend(struct rmi_device *rmi_dev) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", retval); + disable_irq(irq); + if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = enable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to enable irq for wake: %d\n", + retval); + } return retval; } EXPORT_SYMBOL_GPL(rmi_driver_suspend); -int rmi_driver_resume(struct rmi_device *rmi_dev) +int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake) { + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; int retval; + enable_irq(irq); + if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = disable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to disable irq for wake: %d\n", + retval); + } + retval = rmi_resume_functions(rmi_dev); if (retval) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", @@ -831,6 +888,10 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; + + disable_irq(irq); rmi_free_function_list(rmi_dev); @@ -1050,6 +1111,10 @@ static int rmi_driver_probe(struct device *dev) } } + retval = rmi_irq_init(rmi_dev); + if (retval < 0) + goto err_destroy_functions; + if (data->f01_container->dev.driver) /* Driver already bound, so enable ATTN now. */ return enable_sensor(rmi_dev); diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index 6f2e0e4f0296..64a548822da4 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,6 @@ struct rmi_i2c_xport { struct mutex page_mutex; int page; - int irq; - u8 *tx_buf; size_t tx_buf_size; @@ -177,42 +174,6 @@ static const struct rmi_transport_ops rmi_i2c_ops = { .read_block = rmi_i2c_read_block, }; -static irqreturn_t rmi_i2c_irq(int irq, void *dev_id) -{ - struct rmi_i2c_xport *rmi_i2c = dev_id; - struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_i2c_init_irq(struct i2c_client *client) -{ - struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL, - rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name, - rmi_i2c); - if (ret < 0) { - dev_warn(&client->dev, "Failed to register interrupt %d\n", - rmi_i2c->irq); - - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id rmi_i2c_of_match[] = { { .compatible = "syna,rmi4-i2c" }, @@ -240,8 +201,7 @@ static int rmi_i2c_probe(struct i2c_client *client, if (!client->dev.of_node && client_pdata) *pdata = *client_pdata; - if (client->irq > 0) - rmi_i2c->irq = client->irq; + pdata->irq = client->irq; rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", dev_name(&client->dev)); @@ -295,10 +255,6 @@ static int rmi_i2c_probe(struct i2c_client *client, return retval; } - retval = rmi_i2c_init_irq(client); - if (retval < 0) - return retval; - dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n", client->addr); return 0; @@ -322,18 +278,10 @@ static int rmi_i2c_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = enable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -353,15 +301,7 @@ static int rmi_i2c_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = disable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -376,12 +316,10 @@ static int rmi_i2c_runtime_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -401,9 +339,7 @@ static int rmi_i2c_runtime_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 55bd1b34970c..f3e9e488635c 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "rmi_driver.h" @@ -44,8 +43,6 @@ struct rmi_spi_xport { struct mutex page_mutex; int page; - int irq; - u8 *rx_buf; u8 *tx_buf; int xfer_buf_size; @@ -326,41 +323,6 @@ static const struct rmi_transport_ops rmi_spi_ops = { .read_block = rmi_spi_read_block, }; -static irqreturn_t rmi_spi_irq(int irq, void *dev_id) -{ - struct rmi_spi_xport *rmi_spi = dev_id; - struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_spi_init_irq(struct spi_device *spi) -{ - struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL, - rmi_spi_irq, irq_flags | IRQF_ONESHOT, - dev_name(&spi->dev), rmi_spi); - if (ret < 0) { - dev_warn(&spi->dev, "Failed to register interrupt %d\n", - rmi_spi->irq); - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static int rmi_spi_of_probe(struct spi_device *spi, struct rmi_device_platform_data *pdata) @@ -433,8 +395,7 @@ static int rmi_spi_probe(struct spi_device *spi) return retval; } - if (spi->irq > 0) - rmi_spi->irq = spi->irq; + pdata->irq = spi->irq; rmi_spi->spi = spi; mutex_init(&rmi_spi->page_mutex); @@ -465,10 +426,6 @@ static int rmi_spi_probe(struct spi_device *spi) return retval; } - retval = rmi_spi_init_irq(spi); - if (retval < 0) - return retval; - dev_info(&spi->dev, "registered RMI SPI driver\n"); return 0; } @@ -489,17 +446,10 @@ static int rmi_spi_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = enable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } return ret; } @@ -509,15 +459,7 @@ static int rmi_spi_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = disable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -532,12 +474,10 @@ static int rmi_spi_runtime_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - return 0; } @@ -547,9 +487,7 @@ static int rmi_spi_runtime_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index e0aca1476001..5944e6c2470d 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -204,9 +204,11 @@ struct rmi_device_platform_data_spi { * @reset_delay_ms - after issuing a reset command to the touch sensor, the * driver waits a few milliseconds to give the firmware a chance to * to re-initialize. You can override the default wait period here. + * @irq: irq associated with the attn gpio line, or negative */ struct rmi_device_platform_data { int reset_delay_ms; + int irq; struct rmi_device_platform_data_spi spi_data; @@ -352,8 +354,7 @@ struct rmi_driver_data { int rmi_register_transport_device(struct rmi_transport_dev *xport); void rmi_unregister_transport_device(struct rmi_transport_dev *xport); -int rmi_process_interrupt_requests(struct rmi_device *rmi_dev); -int rmi_driver_suspend(struct rmi_device *rmi_dev); -int rmi_driver_resume(struct rmi_device *rmi_dev); +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake); +int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake); #endif -- cgit v1.2.3 From 2775e523246e11c5ce90b69226c5e67aa43e64a5 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 8 Nov 2016 16:48:48 -0800 Subject: Input: synaptics-rmi4 - add parameters for dribble packets and palm detect gesture The rmi_f11 driver currently disables dribble packets and the palm detect gesture for all devices. This patch creates a parameter in the 2d sensor platform data for controlling this functionality on a per device basis. For more information on dribble packets: Commit 05ba999fcabb ("HID: rmi: disable dribble packets on Synaptics touchpads") For more information on the palm detect gesture: Commit f097deef59a6 ("HID: rmi: disable palm detect gesture when present") Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_2d_sensor.h | 2 ++ drivers/input/rmi4/rmi_f01.c | 6 +++--- drivers/input/rmi4/rmi_f11.c | 32 ++++++++++++++++++++++++++++---- include/linux/rmi.h | 21 +++++++++++++-------- 4 files changed, 46 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 77fcdfef003c..c871bef4dac0 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -67,6 +67,8 @@ struct rmi_2d_sensor { u8 report_rel; u8 x_mm; u8 y_mm; + enum rmi_reg_state dribble; + enum rmi_reg_state palm_detect; }; int rmi_2d_sensor_of_probe(struct device *dev, diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index fac81fc9bcf6..2cfa9f64acfb 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -327,12 +327,12 @@ static int rmi_f01_probe(struct rmi_function *fn) } switch (pdata->power_management.nosleep) { - case RMI_F01_NOSLEEP_DEFAULT: + case RMI_REG_STATE_DEFAULT: break; - case RMI_F01_NOSLEEP_OFF: + case RMI_REG_STATE_OFF: f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT; break; - case RMI_F01_NOSLEEP_ON: + case RMI_REG_STATE_ON: f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT; break; } diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 3218742d2d8c..c252d405df4c 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1142,6 +1142,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad; sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking; sensor->dmax = f11->sensor_pdata.dmax; + sensor->dribble = f11->sensor_pdata.dribble; + sensor->palm_detect = f11->sensor_pdata.palm_detect; if (f11->sens_query.has_physical_props) { sensor->x_mm = f11->sens_query.x_sensor_size_mm; @@ -1209,11 +1211,33 @@ static int rmi_f11_initialize(struct rmi_function *fn) ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = sensor->axis_align.delta_y_threshold; - if (f11->sens_query.has_dribble) - ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6); + if (f11->sens_query.has_dribble) { + switch (sensor->dribble) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[0] &= ~BIT(6); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[0] |= BIT(6); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } - if (f11->sens_query.has_palm_det) - ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0); + if (f11->sens_query.has_palm_det) { + switch (sensor->palm_detect) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[11] &= ~BIT(0); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[11] |= BIT(0); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } rc = f11_write_control_regs(fn, &f11->sens_query, &f11->dev_controls, fn->fd.query_base_addr); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 5944e6c2470d..ac904bb439a5 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -99,6 +99,8 @@ struct rmi_2d_sensor_platform_data { bool topbuttonpad; bool kernel_tracking; int dmax; + int dribble; + int palm_detect; }; /** @@ -116,14 +118,17 @@ struct rmi_f30_data { bool disable; }; -/** - * struct rmi_f01_power - override default power management settings. - * + +/* + * Set the state of a register + * DEFAULT - use the default value set by the firmware config + * OFF - explicitly disable the register + * ON - explicitly enable the register */ -enum rmi_f01_nosleep { - RMI_F01_NOSLEEP_DEFAULT = 0, - RMI_F01_NOSLEEP_OFF = 1, - RMI_F01_NOSLEEP_ON = 2 +enum rmi_reg_state { + RMI_REG_STATE_DEFAULT = 0, + RMI_REG_STATE_OFF = 1, + RMI_REG_STATE_ON = 2 }; /** @@ -143,7 +148,7 @@ enum rmi_f01_nosleep { * when the touch sensor is in doze mode, in units of 10ms. */ struct rmi_f01_power_management { - enum rmi_f01_nosleep nosleep; + enum rmi_reg_state nosleep; u8 wakeup_threshold; u8 doze_holdoff; u8 doze_interval; -- cgit v1.2.3 From 29fd0ec2bdbef6734fd4c39c23f61d9f030a66a0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 22 Nov 2016 17:44:12 -0800 Subject: Input: synaptics-rmi4 - add support for F34 device reflash Add support for updating firmware, triggered by a sysfs attribute. This patch has been tested on Synaptics S7300. Signed-off-by: Nick Dyer Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 11 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.c | 105 ++++++--- drivers/input/rmi4/rmi_driver.h | 24 ++ drivers/input/rmi4/rmi_f01.c | 6 + drivers/input/rmi4/rmi_f34.c | 481 ++++++++++++++++++++++++++++++++++++++++ drivers/input/rmi4/rmi_f34.h | 68 ++++++ include/linux/rmi.h | 2 + 9 files changed, 670 insertions(+), 31 deletions(-) create mode 100644 drivers/input/rmi4/rmi_f34.c create mode 100644 drivers/input/rmi4/rmi_f34.h (limited to 'include') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 8cbd362ae8a7..9a24867ae7a5 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -74,6 +74,17 @@ config RMI4_F30 Function 30 provides GPIO and LED support for RMI4 devices. This includes support for buttons on TouchPads and ClickPads. +config RMI4_F34 + bool "RMI4 Function 34 (Device reflash)" + depends on RMI4_CORE + select FW_LOADER + help + Say Y here if you want to add support for RMI4 function 34. + + Function 34 provides support for upgrading the firmware on the RMI4 + device via the firmware loader interface. This is triggered using a + sysfs attribute. + config RMI4_F54 bool "RMI4 Function 54 (Analog diagnostics)" depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index a6e27527d514..0250abf38ad9 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o +rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o # Transports diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 84b321262d74..ef7a66230d83 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif +#ifdef CONFIG_RMI4_F34 + &rmi_f34_handler, +#endif #ifdef CONFIG_RMI4_F54 &rmi_f54_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 4f8d19794b01..2b17d8cb3d10 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -35,14 +35,24 @@ #define RMI_DEVICE_RESET_CMD 0x01 #define DEFAULT_RESET_DELAY_MS 100 -static void rmi_free_function_list(struct rmi_device *rmi_dev) +void rmi_free_function_list(struct rmi_device *rmi_dev) { struct rmi_function *fn, *tmp; struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n"); + mutex_lock(&data->irq_mutex); + + devm_kfree(&rmi_dev->dev, data->irq_memory); + data->irq_memory = NULL; + data->irq_status = NULL; + data->fn_irq_bits = NULL; + data->current_irq_mask = NULL; + data->new_irq_mask = NULL; + data->f01_container = NULL; + data->f34_container = NULL; /* Doing it in the reverse order so F01 will be removed last */ list_for_each_entry_safe_reverse(fn, tmp, @@ -50,7 +60,10 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev) list_del(&fn->node); rmi_unregister_function(fn); } + + mutex_unlock(&data->irq_mutex); } +EXPORT_SYMBOL_GPL(rmi_free_function_list); static int reset_one_function(struct rmi_function *fn) { @@ -147,24 +160,25 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; + mutex_lock(&data->irq_mutex); + if (!data->irq_status || !data->f01_container) { + mutex_unlock(&data->irq_mutex); + return 0; + } + if (!rmi_dev->xport->attn_data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); if (error < 0) { dev_err(dev, "Failed to read irqs, code=%d\n", error); + mutex_unlock(&data->irq_mutex); return error; } } - mutex_lock(&data->irq_mutex); bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, data->irq_count); - /* - * At this point, irq_status has all bits that are set in the - * interrupt status register and are enabled. - */ - mutex_unlock(&data->irq_mutex); /* * It would be nice to be able to use irq_chip to handle these @@ -180,6 +194,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (data->input) input_sync(data->input); + mutex_unlock(&data->irq_mutex); + return 0; } @@ -244,12 +260,18 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; + mutex_lock(&data->irq_mutex); + list_for_each_entry(entry, &data->function_list, node) { retval = suspend_one_function(entry); - if (retval < 0) + if (retval < 0) { + mutex_unlock(&data->irq_mutex); return retval; + } } + mutex_unlock(&data->irq_mutex); + return 0; } @@ -278,16 +300,22 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; + mutex_lock(&data->irq_mutex); + list_for_each_entry(entry, &data->function_list, node) { retval = resume_one_function(entry); - if (retval < 0) + if (retval < 0) { + mutex_unlock(&data->irq_mutex); return retval; + } } + mutex_unlock(&data->irq_mutex); + return 0; } -static int enable_sensor(struct rmi_device *rmi_dev) +int rmi_enable_sensor(struct rmi_device *rmi_dev) { int retval = 0; @@ -297,6 +325,7 @@ static int enable_sensor(struct rmi_device *rmi_dev) return rmi_process_interrupt_requests(rmi_dev); } +EXPORT_SYMBOL_GPL(rmi_enable_sensor); /** * rmi_driver_set_input_params - set input device id and other data. @@ -502,10 +531,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, RMI_SCAN_DONE : RMI_SCAN_CONTINUE; } -static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, - int (*callback)(struct rmi_device *rmi_dev, - void *ctx, - const struct pdt_entry *entry)) +int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *entry)) { int page; int empty_pages = 0; @@ -520,6 +548,7 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, return retval < 0 ? retval : 0; } +EXPORT_SYMBOL_GPL(rmi_scan_pdt); int rmi_read_register_desc(struct rmi_device *d, u16 addr, struct rmi_register_descriptor *rdesc) @@ -740,19 +769,15 @@ static int rmi_count_irqs(struct rmi_device *rmi_dev, int *irq_count = ctx; *irq_count += pdt->interrupt_source_count; - if (pdt->function_number == 0x01) { + if (pdt->function_number == 0x01) data->f01_bootloader_mode = rmi_check_bootloader_mode(rmi_dev, pdt); - if (data->f01_bootloader_mode) - dev_warn(&rmi_dev->dev, - "WARNING: RMI4 device is in bootloader mode!\n"); - } return RMI_SCAN_CONTINUE; } -static int rmi_initial_reset(struct rmi_device *rmi_dev, - void *ctx, const struct pdt_entry *pdt) +int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *pdt) { int error; @@ -787,6 +812,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev, /* F01 should always be on page 0. If we don't find it there, fail. */ return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; } +EXPORT_SYMBOL_GPL(rmi_initial_reset); static int rmi_create_function(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) @@ -828,6 +854,8 @@ static int rmi_create_function(struct rmi_device *rmi_dev, if (pdt->function_number == 0x01) data->f01_container = fn; + else if (pdt->function_number == 0x34) + data->f34_container = fn; list_add_tail(&fn->node, &data->function_list); @@ -893,6 +921,7 @@ static int rmi_driver_remove(struct device *dev) disable_irq(irq); + rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); return 0; @@ -919,13 +948,12 @@ static inline int rmi_driver_of_probe(struct device *dev, } #endif -static int rmi_probe_interrupts(struct rmi_driver_data *data) +int rmi_probe_interrupts(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; int irq_count; size_t size; - void *irq_memory; int retval; /* @@ -941,31 +969,38 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data) dev_err(dev, "IRQ counting failed with code %d.\n", retval); return retval; } + + if (data->f01_bootloader_mode) + dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n"); + data->irq_count = irq_count; data->num_of_irq_regs = (data->irq_count + 7) / 8; size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); - irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); - if (!irq_memory) { + data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!data->irq_memory) { dev_err(dev, "Failed to allocate memory for irq masks.\n"); return retval; } - data->irq_status = irq_memory + size * 0; - data->fn_irq_bits = irq_memory + size * 1; - data->current_irq_mask = irq_memory + size * 2; - data->new_irq_mask = irq_memory + size * 3; + data->irq_status = data->irq_memory + size * 0; + data->fn_irq_bits = data->irq_memory + size * 1; + data->current_irq_mask = data->irq_memory + size * 2; + data->new_irq_mask = data->irq_memory + size * 3; return retval; } +EXPORT_SYMBOL_GPL(rmi_probe_interrupts); -static int rmi_init_functions(struct rmi_driver_data *data) +int rmi_init_functions(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; int irq_count; int retval; + mutex_lock(&data->irq_mutex); + irq_count = 0; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); @@ -990,12 +1025,16 @@ static int rmi_init_functions(struct rmi_driver_data *data) goto err_destroy_functions; } + mutex_unlock(&data->irq_mutex); + return 0; err_destroy_functions: rmi_free_function_list(rmi_dev); + mutex_unlock(&data->irq_mutex); return retval; } +EXPORT_SYMBOL_GPL(rmi_init_functions); static int rmi_driver_probe(struct device *dev) { @@ -1100,6 +1139,10 @@ static int rmi_driver_probe(struct device *dev) if (retval) goto err; + retval = rmi_f34_create_sysfs(rmi_dev); + if (retval) + goto err; + if (data->input) { rmi_driver_set_input_name(rmi_dev, data->input); if (!rmi_dev->xport->input) { @@ -1117,7 +1160,7 @@ static int rmi_driver_probe(struct device *dev) if (data->f01_container->dev.driver) /* Driver already bound, so enable ATTN now. */ - return enable_sensor(rmi_dev); + return rmi_enable_sensor(rmi_dev); return 0; diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 8dfbebe9bf86..e627a3a8aced 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -95,12 +95,36 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, bool rmi_is_physical_driver(struct device_driver *); int rmi_register_physical_driver(void); void rmi_unregister_physical_driver(void); +void rmi_free_function_list(struct rmi_device *rmi_dev); +int rmi_enable_sensor(struct rmi_device *rmi_dev); +int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *entry)); +int rmi_probe_interrupts(struct rmi_driver_data *data); +int rmi_init_functions(struct rmi_driver_data *data); +int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *pdt); char *rmi_f01_get_product_ID(struct rmi_function *fn); +#ifdef CONFIG_RMI4_F34 +int rmi_f34_create_sysfs(struct rmi_device *rmi_dev); +void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev); +#else +static inline int rmi_f34_create_sysfs(struct rmi_device *rmi_dev) +{ + return 0; +} + +static inline void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) +{ +} +#endif /* CONFIG_RMI_F34 */ + extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; +extern struct rmi_function_handler rmi_f34_handler; extern struct rmi_function_handler rmi_f54_handler; #endif diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 2cfa9f64acfb..cae35c6cde31 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -63,6 +63,8 @@ struct f01_basic_properties { #define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) /* The device has lost its configuration for some reason. */ #define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) +/* The device is in bootloader mode */ +#define RMI_F01_STATUS_BOOTLOADER(status) ((status) & 0x40) /* Control register bits */ @@ -594,6 +596,10 @@ static int rmi_f01_attention(struct rmi_function *fn, return error; } + if (RMI_F01_STATUS_BOOTLOADER(device_status)) + dev_warn(&fn->dev, + "Device in bootloader mode, please update firmware\n"); + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { dev_warn(&fn->dev, "Device reset detected.\n"); error = rmi_dev->driver->reset_handler(rmi_dev); diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c new file mode 100644 index 000000000000..03df85ac91a5 --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * 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 +#include +#include +#include +#include + +#include "rmi_driver.h" +#include "rmi_f34.h" + +static int rmi_f34_write_bootloader_id(struct f34_data *f34) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + u8 bootloader_id[F34_BOOTLOADER_ID_LEN]; + int ret; + + ret = rmi_read_block(rmi_dev, fn->fd.query_base_addr, + bootloader_id, sizeof(bootloader_id)); + if (ret) { + dev_err(&fn->dev, "%s: Reading bootloader ID failed: %d\n", + __func__, ret); + return ret; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: writing bootloader id '%c%c'\n", + __func__, bootloader_id[0], bootloader_id[1]); + + ret = rmi_write_block(rmi_dev, + fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET, + bootloader_id, sizeof(bootloader_id)); + if (ret) { + dev_err(&fn->dev, "Failed to write bootloader ID: %d\n", ret); + return ret; + } + + return 0; +} + +static int rmi_f34_command(struct f34_data *f34, u8 command, + unsigned int timeout, bool write_bl_id) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + int ret; + + if (write_bl_id) { + ret = rmi_f34_write_bootloader_id(f34); + if (ret) + return ret; + } + + init_completion(&f34->v5.cmd_done); + + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + if (ret) { + dev_err(&f34->fn->dev, + "%s: Failed to read cmd register: %d (command %#02x)\n", + __func__, ret, command); + return ret; + } + + f34->v5.status |= command & 0x0f; + + ret = rmi_write(rmi_dev, f34->v5.ctrl_address, f34->v5.status); + if (ret < 0) { + dev_err(&f34->fn->dev, + "Failed to write F34 command %#02x: %d\n", + command, ret); + return ret; + } + + if (!wait_for_completion_timeout(&f34->v5.cmd_done, + msecs_to_jiffies(timeout))) { + + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + if (ret) { + dev_err(&f34->fn->dev, + "%s: cmd %#02x timed out: %d\n", + __func__, command, ret); + return ret; + } + + if (f34->v5.status & 0x7f) { + dev_err(&f34->fn->dev, + "%s: cmd %#02x timed out, status: %#02x\n", + __func__, command, f34->v5.status); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f34_data *f34 = dev_get_drvdata(&fn->dev); + int ret; + + ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n", + __func__, f34->v5.status, ret); + + if (!ret && !(f34->v5.status & 0x7f)) + complete(&f34->v5.cmd_done); + + return 0; +} + +static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, + int block_count, u8 command) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + u16 address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET; + u8 start_address[] = { 0, 0 }; + int i; + int ret; + + ret = rmi_write_block(rmi_dev, fn->fd.data_base_addr, + start_address, sizeof(start_address)); + if (ret) { + dev_err(&fn->dev, "Failed to write initial zeros: %d\n", ret); + return ret; + } + + for (i = 0; i < block_count; i++) { + ret = rmi_write_block(rmi_dev, address, + data, f34->v5.block_size); + if (ret) { + dev_err(&fn->dev, + "failed to write block #%d: %d\n", i, ret); + return ret; + } + + ret = rmi_f34_command(f34, command, F34_IDLE_WAIT_MS, false); + if (ret) { + dev_err(&fn->dev, + "Failed to write command for block #%d: %d\n", + i, ret); + return ret; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "wrote block %d of %d\n", + i + 1, block_count); + + data += f34->v5.block_size; + } + + return 0; +} + +static int rmi_f34_write_firmware(struct f34_data *f34, const void *data) +{ + return rmi_f34_write_blocks(f34, data, f34->v5.fw_blocks, + F34_WRITE_FW_BLOCK); +} + +static int rmi_f34_write_config(struct f34_data *f34, const void *data) +{ + return rmi_f34_write_blocks(f34, data, f34->v5.config_blocks, + F34_WRITE_CONFIG_BLOCK); +} + +int rmi_f34_enable_flash(struct f34_data *f34) +{ + return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG, + F34_ENABLE_WAIT_MS, true); +} + +static int rmi_f34_flash_firmware(struct f34_data *f34, + const struct rmi_f34_firmware *syn_fw) +{ + struct rmi_function *fn = f34->fn; + int ret; + + if (syn_fw->image_size) { + dev_info(&fn->dev, "Erasing firmware...\n"); + ret = rmi_f34_command(f34, F34_ERASE_ALL, + F34_ERASE_WAIT_MS, true); + if (ret) + return ret; + + dev_info(&fn->dev, "Writing firmware (%d bytes)...\n", + syn_fw->image_size); + ret = rmi_f34_write_firmware(f34, syn_fw->data); + if (ret) + return ret; + } + + if (syn_fw->config_size) { + /* + * We only need to erase config if we haven't updated + * firmware. + */ + if (!syn_fw->image_size) { + dev_info(&fn->dev, "Erasing config...\n"); + ret = rmi_f34_command(f34, F34_ERASE_CONFIG, + F34_ERASE_WAIT_MS, true); + if (ret) + return ret; + } + + dev_info(&fn->dev, "Writing config (%d bytes)...\n", + syn_fw->config_size); + ret = rmi_f34_write_config(f34, + &syn_fw->data[syn_fw->image_size]); + if (ret) + return ret; + } + + return 0; +} + +int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw) +{ + const struct rmi_f34_firmware *syn_fw; + int ret; + + syn_fw = (const struct rmi_f34_firmware *)fw->data; + BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) != + F34_FW_IMAGE_OFFSET); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n", + (int)fw->size, + le32_to_cpu(syn_fw->checksum), + le32_to_cpu(syn_fw->image_size), + le32_to_cpu(syn_fw->config_size)); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n", + syn_fw->bootloader_version, + (int)sizeof(syn_fw->product_id), syn_fw->product_id, + syn_fw->product_info[0], syn_fw->product_info[1]); + + if (syn_fw->image_size && + syn_fw->image_size != f34->v5.fw_blocks * f34->v5.block_size) { + dev_err(&f34->fn->dev, + "Bad firmware image: fw size %d, expected %d\n", + syn_fw->image_size, + f34->v5.fw_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->config_size && + syn_fw->config_size != f34->v5.config_blocks * f34->v5.block_size) { + dev_err(&f34->fn->dev, + "Bad firmware image: config size %d, expected %d\n", + syn_fw->config_size, + f34->v5.config_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->image_size && !syn_fw->config_size) { + dev_err(&f34->fn->dev, "Bad firmware image: no config data\n"); + ret = -EILSEQ; + goto out; + } + + dev_info(&f34->fn->dev, "Firmware image OK\n"); + mutex_lock(&f34->v5.flash_mutex); + + ret = rmi_f34_flash_firmware(f34, syn_fw); + + mutex_unlock(&f34->v5.flash_mutex); + +out: + return ret; +} + +static int rmi_firmware_update(struct rmi_driver_data *data, + const struct firmware *fw) +{ + struct device *dev = &data->rmi_dev->dev; + struct f34_data *f34; + int ret; + + if (!data->f34_container) { + dev_warn(dev, "%s: No F34 present!\n", __func__); + return -EINVAL; + } + + /* Only version 0 currently supported */ + if (data->f34_container->fd.function_version != 0) { + dev_warn(dev, "F34 V%d not supported!\n", + data->f34_container->fd.function_version); + return -ENODEV; + } + + f34 = dev_get_drvdata(&data->f34_container->dev); + + /* Enter flash mode */ + ret = rmi_f34_enable_flash(f34); + if (ret) + return ret; + + /* Tear down functions and re-probe */ + rmi_free_function_list(data->rmi_dev); + + ret = rmi_probe_interrupts(data); + if (ret) + return ret; + + ret = rmi_init_functions(data); + if (ret) + return ret; + + if (!data->f01_bootloader_mode || !data->f34_container) { + dev_warn(dev, "%s: No F34 present or not in bootloader!\n", + __func__); + return -EINVAL; + } + + f34 = dev_get_drvdata(&data->f34_container->dev); + + /* Perform firmware update */ + ret = rmi_f34_update_firmware(f34, fw); + + dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); + + /* Re-probe */ + rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n"); + rmi_free_function_list(data->rmi_dev); + + ret = rmi_scan_pdt(data->rmi_dev, NULL, rmi_initial_reset); + if (ret < 0) + dev_warn(dev, "RMI reset failed!\n"); + + ret = rmi_probe_interrupts(data); + if (ret) + return ret; + + ret = rmi_init_functions(data); + if (ret) + return ret; + + if (data->f01_container->dev.driver) + /* Driver already bound, so enable ATTN now. */ + return rmi_enable_sensor(data->rmi_dev); + + rmi_dbg(RMI_DEBUG_FN, dev, "%s complete\n", __func__); + + return ret; +} + +static ssize_t rmi_driver_update_fw_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + char fw_name[NAME_MAX]; + const struct firmware *fw; + size_t copy_count = count; + int ret; + + if (count == 0 || count >= NAME_MAX) + return -EINVAL; + + if (buf[count - 1] == '\0' || buf[count - 1] == '\n') + copy_count -= 1; + + strncpy(fw_name, buf, copy_count); + fw_name[copy_count] = '\0'; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) + return ret; + + dev_info(dev, "Flashing %s\n", fw_name); + + ret = rmi_firmware_update(data, fw); + + release_firmware(fw); + + return ret ?: count; +} + +static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store); + +static struct attribute *rmi_firmware_attrs[] = { + &dev_attr_update_fw.attr, + NULL +}; + +static struct attribute_group rmi_firmware_attr_group = { + .attrs = rmi_firmware_attrs, +}; + +static int rmi_f34_probe(struct rmi_function *fn) +{ + struct f34_data *f34; + unsigned char f34_queries[9]; + bool has_config_id; + int ret; + + f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL); + if (!f34) + return -ENOMEM; + + f34->fn = fn; + dev_set_drvdata(&fn->dev, f34); + + ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + f34_queries, sizeof(f34_queries)); + if (ret) { + dev_err(&fn->dev, "%s: Failed to query properties\n", + __func__); + return ret; + } + + snprintf(f34->bootloader_id, sizeof(f34->bootloader_id), + "%c%c", f34_queries[0], f34_queries[1]); + + mutex_init(&f34->v5.flash_mutex); + init_completion(&f34->v5.cmd_done); + + f34->v5.block_size = get_unaligned_le16(&f34_queries[3]); + f34->v5.fw_blocks = get_unaligned_le16(&f34_queries[5]); + f34->v5.config_blocks = get_unaligned_le16(&f34_queries[7]); + f34->v5.ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET + + f34->v5.block_size; + has_config_id = f34_queries[2] & (1 << 2); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Bootloader ID: %s\n", + f34->bootloader_id); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", + f34->v5.block_size); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", + f34->v5.fw_blocks); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", + f34->v5.config_blocks); + + if (has_config_id) { + ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr, + f34_queries, sizeof(f34_queries)); + if (ret) { + dev_err(&fn->dev, "Failed to read F34 config ID\n"); + return ret; + } + + snprintf(f34->configuration_id, sizeof(f34->configuration_id), + "%02x%02x%02x%02x", + f34_queries[0], f34_queries[1], + f34_queries[2], f34_queries[3]); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Configuration ID: %s\n", + f34->configuration_id); + } + + return 0; +} + +int rmi_f34_create_sysfs(struct rmi_device *rmi_dev) +{ + return sysfs_create_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group); +} + +void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) +{ + sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group); +} + +struct rmi_function_handler rmi_f34_handler = { + .driver = { + .name = "rmi4_f34", + }, + .func = 0x34, + .probe = rmi_f34_probe, + .attention = rmi_f34_attention, +}; diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h new file mode 100644 index 000000000000..6cee5282fbb4 --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * 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. + */ + +#ifndef _RMI_F34_H +#define _RMI_F34_H + +/* F34 image file offsets. */ +#define F34_FW_IMAGE_OFFSET 0x100 + +/* F34 register offsets. */ +#define F34_BLOCK_DATA_OFFSET 2 + +/* F34 commands */ +#define F34_WRITE_FW_BLOCK 0x2 +#define F34_ERASE_ALL 0x3 +#define F34_READ_CONFIG_BLOCK 0x5 +#define F34_WRITE_CONFIG_BLOCK 0x6 +#define F34_ERASE_CONFIG 0x7 +#define F34_ENABLE_FLASH_PROG 0xf + +#define F34_STATUS_IN_PROGRESS 0xff +#define F34_STATUS_IDLE 0x80 + +#define F34_IDLE_WAIT_MS 500 +#define F34_ENABLE_WAIT_MS 300 +#define F34_ERASE_WAIT_MS 5000 + +#define F34_BOOTLOADER_ID_LEN 2 + +struct rmi_f34_firmware { + __le32 checksum; + u8 pad1[3]; + u8 bootloader_version; + __le32 image_size; + __le32 config_size; + u8 product_id[10]; + u8 product_info[2]; + u8 pad2[228]; + u8 data[]; +}; + +struct f34v5_data { + u16 block_size; + u16 fw_blocks; + u16 config_blocks; + u16 ctrl_address; + u8 status; + + struct completion cmd_done; + struct mutex flash_mutex; +}; + +struct f34_data { + struct rmi_function *fn; + + unsigned char bootloader_id[5]; + unsigned char configuration_id[9]; + + struct f34v5_data v5; +}; + +#endif /* _RMI_F34_H */ diff --git a/include/linux/rmi.h b/include/linux/rmi.h index ac904bb439a5..4096b0246c23 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -337,11 +337,13 @@ struct rmi_driver_data { struct rmi_device *rmi_dev; struct rmi_function *f01_container; + struct rmi_function *f34_container; bool f01_bootloader_mode; u32 attn_count; int num_of_irq_regs; int irq_count; + void *irq_memory; unsigned long *irq_status; unsigned long *fn_irq_bits; unsigned long *current_irq_mask; -- cgit v1.2.3 From c762cc68b6a12eedebefc156ea4838e54804e2eb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Nov 2016 17:57:02 -0800 Subject: Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54 F54 diagnostics report functions provide data based on the number of enabled rx and tx electrodes, which is not identical to the number of electrodes reported with F54:Query0 and F54:Query1. Those values report the number of supported electrodes, not the number of enabled electrodes. The number of enabled electrodes can be determined by analyzing F55:Ctrl1 (sensor receiver assignment) and F55:Ctrl2 (sensor transmitter assignment). Propagate the number of enabled electrodes from F55 to F54 to avoid corrupted output if not all electrodes are enabled. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 ...") Signed-off-by: Guenter Roeck Tested-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 1 + drivers/input/rmi4/rmi_f54.c | 14 ++++++++++---- drivers/input/rmi4/rmi_f55.c | 7 +++++++ include/linux/rmi.h | 3 +++ 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f4460f3ebac5..a9c36a5fe708 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -90,6 +90,7 @@ config RMI4_F54 depends on RMI4_CORE depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC + select RMI4_F55 help Say Y here if you want to add support for RMI4 function 54 diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 2e934aef3d2a..dea63e2db3e6 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -217,8 +217,10 @@ unlock: static size_t rmi_f54_get_report_size(struct f54_data *f54) { - u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes; - u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes; + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; size_t size; switch (rmi_f54_get_reptype(f54, f54->input)) { @@ -402,6 +404,10 @@ static int rmi_f54_vidioc_enum_input(struct file *file, void *priv, static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) { + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; struct v4l2_pix_format *f = &f54->format; enum rmi_f54_report_type reptype; int ret; @@ -416,8 +422,8 @@ static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) f54->input = i; - f->width = f54->num_rx_electrodes; - f->height = f54->num_tx_electrodes; + f->width = rx; + f->height = tx; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; f->bytesperline = f->width * sizeof(u16); diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c index 2d221cc97391..37390ca6a924 100644 --- a/drivers/input/rmi4/rmi_f55.c +++ b/drivers/input/rmi4/rmi_f55.c @@ -38,6 +38,8 @@ struct f55_data { static int rmi_f55_detect(struct rmi_function *fn) { + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); struct f55_data *f55; int error; @@ -57,6 +59,9 @@ static int rmi_f55_detect(struct rmi_function *fn) f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes; + drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes; + if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { int i, total; u8 buf[256]; @@ -78,6 +83,7 @@ static int rmi_f55_detect(struct rmi_function *fn) total++; } f55->cfg_num_rx_electrodes = total; + drv_data->num_rx_electrodes = total; } error = rmi_read_block(fn->rmi_dev, @@ -90,6 +96,7 @@ static int rmi_f55_detect(struct rmi_function *fn) total++; } f55->cfg_num_tx_electrodes = total; + drv_data->num_tx_electrodes = total; } } diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 4096b0246c23..8499b6aa2221 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -354,6 +354,9 @@ struct rmi_driver_data { u8 pdt_props; u8 bsr; + u8 num_rx_electrodes; + u8 num_tx_electrodes; + bool enabled; void *data; -- cgit v1.2.3 From 9e421b7c60860f150aa29e9f7bc902f003d87e68 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2016 17:41:50 -0800 Subject: Input: synaptics-rmi4 - fix documentation of rmi_2d_sensor_platform_data Typos... Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- include/linux/rmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 8499b6aa2221..27dd9aa4090c 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -108,7 +108,7 @@ struct rmi_2d_sensor_platform_data { * @buttonpad - the touchpad is a buttonpad, so enable only the first actual * button that is found. * @trackstick_buttons - Set when the function 30 is handling the physical - * buttons of the trackstick (as a PD/2 passthrough device. + * buttons of the trackstick (as a PS/2 passthrough device). * @disable - the touchpad incorrectly reports F30 and it should be ignored. * This is a special case which is due to misconfigured firmware. */ -- cgit v1.2.3 From 0d37d63a001202b4932f6b14b05d8d055a0a45b6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2016 17:42:01 -0800 Subject: Input: synaptics-rmi4 - remove unused fields in struct rmi_driver_data These fields are not used anywhere, there is no point in carrying them. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- include/linux/rmi.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 27dd9aa4090c..0b118ab47b8d 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -340,7 +340,6 @@ struct rmi_driver_data { struct rmi_function *f34_container; bool f01_bootloader_mode; - u32 attn_count; int num_of_irq_regs; int irq_count; void *irq_memory; @@ -352,14 +351,11 @@ struct rmi_driver_data { struct input_dev *input; u8 pdt_props; - u8 bsr; u8 num_rx_electrodes; u8 num_tx_electrodes; bool enabled; - - void *data; }; int rmi_register_transport_device(struct rmi_transport_dev *xport); -- cgit v1.2.3 From a64ea311f1e4bc090c89960650637423e86c35c0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2016 17:42:13 -0800 Subject: Input: synaptics-rmi4 - add rmi_enable/disable_irq Set the .enabled boolean and trigger an event processing when enabling for edge-triggered systems. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 83 +++++++++++++++++++++++++++++++---------- drivers/input/rmi4/rmi_driver.h | 2 + include/linux/rmi.h | 1 + 3 files changed, 67 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 2b17d8cb3d10..f04fc4152c1f 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -215,6 +215,7 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id) static int rmi_irq_init(struct rmi_device *rmi_dev) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int irq_flags = irq_get_trigger_type(pdata->irq); int ret; @@ -232,6 +233,8 @@ static int rmi_irq_init(struct rmi_device *rmi_dev) return ret; } + data->enabled = true; + return 0; } @@ -866,17 +869,54 @@ err_put_fn: return error; } -int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int irq = pdata->irq; - int retval = 0; + int irq_flags; + int retval; - retval = rmi_suspend_functions(rmi_dev); - if (retval) - dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", - retval); + mutex_lock(&data->enabled_mutex); + + if (data->enabled) + goto out; + + enable_irq(irq); + data->enabled = true; + if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = disable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to disable irq for wake: %d\n", + retval); + } + + /* + * Call rmi_process_interrupt_requests() after enabling irq, + * otherwise we may lose interrupt on edge-triggered systems. + */ + irq_flags = irq_get_trigger_type(pdata->irq); + if (irq_flags & IRQ_TYPE_EDGE_BOTH) + rmi_process_interrupt_requests(rmi_dev); + +out: + mutex_unlock(&data->enabled_mutex); +} + +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int irq = pdata->irq; + int retval; + + mutex_lock(&data->enabled_mutex); + + if (!data->enabled) + goto out; + data->enabled = false; disable_irq(irq); if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) { retval = enable_irq_wake(irq); @@ -885,24 +925,30 @@ int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) "Failed to enable irq for wake: %d\n", retval); } + +out: + mutex_unlock(&data->enabled_mutex); +} + +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) +{ + int retval; + + retval = rmi_suspend_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + rmi_disable_irq(rmi_dev, enable_wake); return retval; } EXPORT_SYMBOL_GPL(rmi_driver_suspend); int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake) { - struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - int irq = pdata->irq; int retval; - enable_irq(irq); - if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { - retval = disable_irq_wake(irq); - if (!retval) - dev_warn(&rmi_dev->dev, - "Failed to disable irq for wake: %d\n", - retval); - } + rmi_enable_irq(rmi_dev, clear_wake); retval = rmi_resume_functions(rmi_dev); if (retval) @@ -916,10 +962,8 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); - struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - int irq = pdata->irq; - disable_irq(irq); + rmi_disable_irq(rmi_dev, false); rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); @@ -1108,6 +1152,7 @@ static int rmi_driver_probe(struct device *dev) } mutex_init(&data->irq_mutex); + mutex_init(&data->enabled_mutex); retval = rmi_probe_interrupts(data); if (retval) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 5b201f369505..c9fe3d3deef3 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -101,6 +101,8 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, int (*callback)(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *entry)); int rmi_probe_interrupts(struct rmi_driver_data *data); +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake); +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake); int rmi_init_functions(struct rmi_driver_data *data); int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 0b118ab47b8d..621f098f1243 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -356,6 +356,7 @@ struct rmi_driver_data { u8 num_tx_electrodes; bool enabled; + struct mutex enabled_mutex; }; int rmi_register_transport_device(struct rmi_transport_dev *xport); -- cgit v1.2.3 From 0a135b88bceac40d0036e401c19cdbda65b38a8f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 30 Nov 2016 17:01:50 -0800 Subject: Input: synaptics-rmi4 - have only one struct platform data If struct rmi_device_platform_data contains pointers to other struct, it gets difficult to allocate a fixed size struct and copy it over between drivers. Change the pointers into a struct and change the code in rmi4 accordingly. Reviewed-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f11.c | 4 ++-- drivers/input/rmi4/rmi_f12.c | 4 ++-- drivers/input/rmi4/rmi_f30.c | 9 ++++----- include/linux/rmi.h | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index c252d405df4c..ffcbbc1745de 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1080,8 +1080,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) rc = rmi_2d_sensor_of_probe(&fn->dev, &f11->sensor_pdata); if (rc) return rc; - } else if (pdata->sensor_pdata) { - f11->sensor_pdata = *pdata->sensor_pdata; + } else { + f11->sensor_pdata = pdata->sensor_pdata; } f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index d7255f18306d..82a4964e5eb9 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -352,8 +352,8 @@ static int rmi_f12_probe(struct rmi_function *fn) ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); if (ret) return ret; - } else if (pdata->sensor_pdata) { - f12->sensor_pdata = *pdata->sensor_pdata; + } else { + f12->sensor_pdata = pdata->sensor_pdata; } ret = rmi_read_register_desc(rmi_dev, query_addr, diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 485907ff10f4..f696137a56f5 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -196,7 +196,7 @@ static int rmi_f30_config(struct rmi_function *fn) rmi_get_platform_data(fn->rmi_dev); int error; - if (pdata->f30_data && pdata->f30_data->disable) { + if (pdata->f30_data.disable) { drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); } else { /* Write Control Register values back to device */ @@ -355,7 +355,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) f30->gpioled_key_map = (u16 *)map_memory; pdata = rmi_get_platform_data(rmi_dev); - if (pdata && f30->has_gpio) { + if (f30->has_gpio) { button = BTN_LEFT; for (i = 0; i < f30->gpioled_count; i++) { if (rmi_f30_is_valid_button(i, f30->ctrl)) { @@ -366,8 +366,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) * f30->has_mech_mouse_btns, but I am * not sure, so use only the pdata info */ - if (pdata->f30_data && - pdata->f30_data->buttonpad) + if (pdata->f30_data.buttonpad) break; } } @@ -382,7 +381,7 @@ static int rmi_f30_probe(struct rmi_function *fn) const struct rmi_device_platform_data *pdata = rmi_get_platform_data(fn->rmi_dev); - if (pdata->f30_data && pdata->f30_data->disable) + if (pdata->f30_data.disable) return 0; rc = rmi_f30_initialize(fn); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 621f098f1243..7780e40a2573 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -218,9 +218,9 @@ struct rmi_device_platform_data { struct rmi_device_platform_data_spi spi_data; /* function handler pdata */ - struct rmi_2d_sensor_platform_data *sensor_pdata; + struct rmi_2d_sensor_platform_data sensor_pdata; struct rmi_f01_power_management power_management; - struct rmi_f30_data *f30_data; + struct rmi_f30_data f30_data; }; /** -- cgit v1.2.3 From b908d3cd812abe3f4a74d7550bbf0a8cbcfbe6ed Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 2 Dec 2016 17:48:51 -0800 Subject: Input: synaptics-rmi4 - allow to add attention data The HID implementation of RMI4 provides the data during the interrupt (in the input report). We need to provide a way for this transport driver to provide the attention data while calling an IRQ. We use a fifo in rmi_core to not lose any incoming event. Signed-off-by: Benjamin Tissoires Reviewed-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 49 +++++++++++++++++++++++++++++++++++++++-- include/linux/rmi.h | 11 +++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index a718e51afb0b..85062e414e73 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -191,16 +191,53 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) return 0; } +void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, + void *data, size_t size) +{ + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data; + void *fifo_data; + + if (!drvdata->enabled) + return; + + fifo_data = kmemdup(data, size, GFP_ATOMIC); + if (!fifo_data) + return; + + attn_data.irq_status = irq_status; + attn_data.size = size; + attn_data.data = fifo_data; + + kfifo_put(&drvdata->attn_fifo, attn_data); +} +EXPORT_SYMBOL_GPL(rmi_set_attn_data); + static irqreturn_t rmi_irq_fn(int irq, void *dev_id) { struct rmi_device *rmi_dev = dev_id; - int ret; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; + int ret, count; + + count = kfifo_get(&drvdata->attn_fifo, &attn_data); + if (count) { + *(drvdata->irq_status) = attn_data.irq_status; + rmi_dev->xport->attn_data = attn_data.data; + rmi_dev->xport->attn_size = attn_data.size; + } ret = rmi_process_interrupt_requests(rmi_dev); if (ret) rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Failed to process interrupt request: %d\n", ret); + if (count) + kfree(attn_data.data); + + if (!kfifo_is_empty(&drvdata->attn_fifo)) + return rmi_irq_fn(irq, dev_id); + return IRQ_HANDLED; } @@ -880,8 +917,9 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; int irq = pdata->irq; - int retval; + int retval, count; mutex_lock(&data->enabled_mutex); @@ -898,6 +936,13 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) retval); } + /* make sure the fifo is clean */ + while (!kfifo_is_empty(&data->attn_fifo)) { + count = kfifo_get(&data->attn_fifo, &attn_data); + if (count) + kfree(attn_data.data); + } + out: mutex_unlock(&data->enabled_mutex); } diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 7780e40a2573..1d4865621493 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -331,6 +332,12 @@ struct rmi_device { }; +struct rmi4_attn_data { + unsigned long irq_status; + size_t size; + void *data; +}; + struct rmi_driver_data { struct list_head function_list; @@ -357,11 +364,15 @@ struct rmi_driver_data { bool enabled; struct mutex enabled_mutex; + DECLARE_KFIFO(attn_fifo, struct rmi4_attn_data, 16); }; int rmi_register_transport_device(struct rmi_transport_dev *xport); void rmi_unregister_transport_device(struct rmi_transport_dev *xport); +void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, + void *data, size_t size); + int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake); int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake); #endif -- cgit v1.2.3 From ae9979c31007d5366b73640ee7dcbb271357053e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 2 Dec 2016 17:49:10 -0800 Subject: Input: synaptics-rmi4 - store the attn data in the driver Now that we have a proper API to set the attention data, there is no point in keeping it in the transport driver. Signed-off-by: Benjamin Tissoires Reviewed-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 5 ++--- drivers/input/rmi4/rmi_f03.c | 13 +++++++------ drivers/input/rmi4/rmi_f11.c | 12 ++++++------ drivers/input/rmi4/rmi_f12.c | 43 +++++++++++++++++++++-------------------- drivers/input/rmi4/rmi_f30.c | 11 ++++++----- include/linux/rmi.h | 5 ++--- 6 files changed, 45 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 85062e414e73..05a3c4bc9778 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -155,7 +155,7 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; - if (!rmi_dev->xport->attn_data) { + if (!data->attn_data.data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); @@ -223,8 +223,7 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id) count = kfifo_get(&drvdata->attn_fifo, &attn_data); if (count) { *(drvdata->irq_status) = attn_data.irq_status; - rmi_dev->xport->attn_data = attn_data.data; - rmi_dev->xport->attn_size = attn_data.size; + drvdata->attn_data = attn_data; } ret = rmi_process_interrupt_requests(rmi_dev); diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 7a3ec0ed0c27..8a7ca3e2f95e 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -164,6 +164,7 @@ static int rmi_f03_config(struct rmi_function *fn) static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f03_data *f03 = dev_get_drvdata(&fn->dev); u16 data_addr = fn->fd.data_base_addr; const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; @@ -174,20 +175,20 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) int i; int error; - if (!rmi_dev || !rmi_dev->xport) + if (!rmi_dev) return -ENODEV; - if (rmi_dev->xport->attn_data) { + if (drvdata->attn_data.data) { /* First grab the data passed by the transport device */ - if (rmi_dev->xport->attn_size < ob_len) { + if (drvdata->attn_data.size < ob_len) { dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); return 0; } - memcpy(obs, rmi_dev->xport->attn_data, ob_len); + memcpy(obs, drvdata->attn_data.data, ob_len); - rmi_dev->xport->attn_data += ob_len; - rmi_dev->xport->attn_size -= ob_len; + drvdata->attn_data.data += ob_len; + drvdata->attn_data.size -= ob_len; } else { /* Grab all of the data registers, and check them for data */ error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index ffcbbc1745de..68279f3c5130 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1285,19 +1285,19 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) int error; int valid_bytes = f11->sensor.pkt_size; - if (rmi_dev->xport->attn_data) { + if (drvdata->attn_data.data) { /* * The valid data in the attention report is less then * expected. Only process the complete fingers. */ - if (f11->sensor.attn_size > rmi_dev->xport->attn_size) - valid_bytes = rmi_dev->xport->attn_size; + if (f11->sensor.attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; else valid_bytes = f11->sensor.attn_size; - memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, + memcpy(f11->sensor.data_pkt, drvdata->attn_data.data, valid_bytes); - rmi_dev->xport->attn_data += f11->sensor.attn_size; - rmi_dev->xport->attn_size -= f11->sensor.attn_size; + drvdata->attn_data.data += f11->sensor.attn_size; + drvdata->attn_data.size -= f11->sensor.attn_size; } else { error = rmi_read_block(rmi_dev, data_base_addr, f11->sensor.data_pkt, diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 82a4964e5eb9..8c5360c25266 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -208,19 +208,20 @@ static int rmi_f12_attention(struct rmi_function *fn, { int retval; struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; int valid_bytes = sensor->pkt_size; - if (rmi_dev->xport->attn_data) { - if (sensor->attn_size > rmi_dev->xport->attn_size) - valid_bytes = rmi_dev->xport->attn_size; + if (drvdata->attn_data.data) { + if (sensor->attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; else valid_bytes = sensor->attn_size; - memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, + memcpy(sensor->data_pkt, drvdata->attn_data.data, valid_bytes); - rmi_dev->xport->attn_data += sensor->attn_size; - rmi_dev->xport->attn_size -= sensor->attn_size; + drvdata->attn_data.data += sensor->attn_size; + drvdata->attn_data.size -= sensor->attn_size; } else { retval = rmi_read_block(rmi_dev, f12->data_addr, sensor->data_pkt, sensor->pkt_size); @@ -323,7 +324,7 @@ static int rmi_f12_probe(struct rmi_function *fn) const struct rmi_register_desc_item *item; struct rmi_2d_sensor *sensor; struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - struct rmi_transport_dev *xport = rmi_dev->xport; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); u16 data_offset = 0; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); @@ -422,7 +423,7 @@ static int rmi_f12_probe(struct rmi_function *fn) * HID attention reports. */ item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); @@ -436,15 +437,15 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); @@ -456,22 +457,22 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data6 = item; f12->data6_offset = data_offset; data_offset += item->reg_size; } item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data9 = item; f12->data9_offset = data_offset; data_offset += item->reg_size; @@ -480,27 +481,27 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data15 = item; f12->data15_offset = data_offset; data_offset += item->reg_size; diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index f696137a56f5..f4b491e3e0fd 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -99,6 +99,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct f30_data *f30 = dev_get_drvdata(&fn->dev); struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); int retval; int gpiled = 0; int value = 0; @@ -109,15 +110,15 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) return 0; /* Read the gpi led data. */ - if (rmi_dev->xport->attn_data) { - if (rmi_dev->xport->attn_size < f30->register_count) { + if (drvdata->attn_data.data) { + if (drvdata->attn_data.size < f30->register_count) { dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); return 0; } - memcpy(f30->data_regs, rmi_dev->xport->attn_data, + memcpy(f30->data_regs, drvdata->attn_data.data, f30->register_count); - rmi_dev->xport->attn_data += f30->register_count; - rmi_dev->xport->attn_size -= f30->register_count; + drvdata->attn_data.data += f30->register_count; + drvdata->attn_data.size -= f30->register_count; } else { retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, f30->data_regs, f30->register_count); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 1d4865621493..ac910f730688 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -272,9 +272,6 @@ struct rmi_transport_dev { struct rmi_device_platform_data pdata; struct input_dev *input; - - void *attn_data; - int attn_size; }; /** @@ -364,6 +361,8 @@ struct rmi_driver_data { bool enabled; struct mutex enabled_mutex; + + struct rmi4_attn_data attn_data; DECLARE_KFIFO(attn_fifo, struct rmi4_attn_data, 16); }; -- cgit v1.2.3 From 5191d88acc688743eef56f1c598a4e4cddf6c6cd Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 10 Dec 2016 23:27:32 -0800 Subject: Input: synaptics-rmi4 - add support for F34 V7 bootloader Port firmware update code from Samsung Galaxy S7 driver into mainline framework. This patch has been tested on Synaptics S7813. Signed-off-by: Nick Dyer Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Makefile | 2 +- drivers/input/rmi4/rmi_driver.c | 56 +- drivers/input/rmi4/rmi_f34.c | 40 +- drivers/input/rmi4/rmi_f34.h | 250 ++++++- drivers/input/rmi4/rmi_f34v7.c | 1372 +++++++++++++++++++++++++++++++++++++++ include/linux/rmi.h | 2 +- 6 files changed, 1688 insertions(+), 34 deletions(-) create mode 100644 drivers/input/rmi4/rmi_f34v7.c (limited to 'include') diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index a199cbe9c27d..9aaac3dd8613 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -8,7 +8,7 @@ rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o -rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o +rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 05a3c4bc9778..cb6efe693302 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -544,7 +544,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, else *empty_pages = 0; - return (data->f01_bootloader_mode || *empty_pages >= 2) ? + return (data->bootloader_mode || *empty_pages >= 2) ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE; } @@ -749,41 +749,49 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, subpacket) == subpacket; } -/* Indicates that flash programming is enabled (bootloader mode). */ -#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40)) - -/* - * Given the PDT entry for F01, read the device status register to determine - * if we're stuck in bootloader mode or not. - * - */ static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev, const struct pdt_entry *pdt) { - int error; - u8 device_status; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int ret; + u8 status; - error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start, - &device_status); - if (error) { - dev_err(&rmi_dev->dev, - "Failed to read device status: %d.\n", error); - return error; + if (pdt->function_number == 0x34 && pdt->function_version > 1) { + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read F34 status: %d.\n", ret); + return ret; + } + + if (status & BIT(7)) + data->bootloader_mode = true; + } else if (pdt->function_number == 0x01) { + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read F01 status: %d.\n", ret); + return ret; + } + + if (status & BIT(6)) + data->bootloader_mode = true; } - return RMI_F01_STATUS_BOOTLOADER(device_status); + return 0; } static int rmi_count_irqs(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) { - struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int *irq_count = ctx; + int ret; *irq_count += pdt->interrupt_source_count; - if (pdt->function_number == 0x01) - data->f01_bootloader_mode = - rmi_check_bootloader_mode(rmi_dev, pdt); + + ret = rmi_check_bootloader_mode(rmi_dev, pdt); + if (ret < 0) + return ret; return RMI_SCAN_CONTINUE; } @@ -1024,13 +1032,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) */ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__); irq_count = 0; + data->bootloader_mode = false; + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); if (retval < 0) { dev_err(dev, "IRQ counting failed with code %d.\n", retval); return retval; } - if (data->f01_bootloader_mode) + if (data->bootloader_mode) dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n"); data->irq_count = irq_count; diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index 01936a4a9a6c..9774dfbab9bb 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "rmi_driver.h" #include "rmi_f34.h" @@ -105,6 +106,9 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) struct f34_data *f34 = dev_get_drvdata(&fn->dev); int ret; + if (f34->bl_version != 5) + return 0; + ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status); rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n", __func__, f34->v5.status, ret); @@ -292,17 +296,24 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return -EINVAL; } - /* Only version 0 currently supported */ - if (data->f34_container->fd.function_version != 0) { + f34 = dev_get_drvdata(&data->f34_container->dev); + + if (f34->bl_version == 7) { + if (data->pdt_props & HAS_BSR) { + dev_err(dev, "%s: LTS not supported\n", __func__); + return -ENODEV; + } + } else if (f34->bl_version != 5) { dev_warn(dev, "F34 V%d not supported!\n", data->f34_container->fd.function_version); return -ENODEV; } - f34 = dev_get_drvdata(&data->f34_container->dev); - /* Enter flash mode */ - ret = rmi_f34_enable_flash(f34); + if (f34->bl_version == 7) + ret = rmi_f34v7_start_reflash(f34, fw); + else + ret = rmi_f34_enable_flash(f34); if (ret) return ret; @@ -319,7 +330,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data, if (ret) return ret; - if (!data->f01_bootloader_mode || !data->f34_container) { + if (!data->bootloader_mode || !data->f34_container) { dev_warn(dev, "%s: No F34 present or not in bootloader!\n", __func__); return -EINVAL; @@ -330,7 +341,10 @@ static int rmi_firmware_update(struct rmi_driver_data *data, f34 = dev_get_drvdata(&data->f34_container->dev); /* Perform firmware update */ - ret = rmi_f34_update_firmware(f34, fw); + if (f34->bl_version == 7) + ret = rmi_f34v7_do_reflash(f34, fw); + else + ret = rmi_f34_update_firmware(f34, fw); dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); @@ -363,6 +377,9 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return ret; } +static int rmi_firmware_update(struct rmi_driver_data *data, + const struct firmware *fw); + static ssize_t rmi_driver_update_fw_store(struct device *dev, struct device_attribute *dattr, const char *buf, size_t count) @@ -411,6 +428,7 @@ static int rmi_f34_probe(struct rmi_function *fn) struct f34_data *f34; unsigned char f34_queries[9]; bool has_config_id; + u8 version = fn->fd.function_version; int ret; f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL); @@ -420,6 +438,14 @@ static int rmi_f34_probe(struct rmi_function *fn) f34->fn = fn; dev_set_drvdata(&fn->dev, f34); + /* v5 code only supported version 0, try V7 probe */ + if (version > 0) + return rmi_f34v7_probe(f34); + else if (version != 0) + return -ENODEV; + + f34->bl_version = 5; + ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, f34_queries, sizeof(f34_queries)); if (ret) { diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h index 6cee5282fbb4..2c21056dc375 100644 --- a/drivers/input/rmi4/rmi_f34.h +++ b/drivers/input/rmi4/rmi_f34.h @@ -33,6 +33,216 @@ #define F34_BOOTLOADER_ID_LEN 2 +/* F34 V7 defines */ +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 +#define V7_BOOTLOADER_ID_OFFSET 1 + +#define IMAGE_HEADER_VERSION_10 0x10 + +#define CONFIG_ID_SIZE 32 +#define PRODUCT_ID_SIZE 10 + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define HAS_BSR BIT(5) +#define HAS_CONFIG_ID BIT(3) +#define HAS_GUEST_CODE BIT(6) +#define HAS_DISP_CFG BIT(5) + +/* F34 V7 commands */ +#define CMD_V7_IDLE 0 +#define CMD_V7_ENTER_BL 1 +#define CMD_V7_READ 2 +#define CMD_V7_WRITE 3 +#define CMD_V7_ERASE 4 +#define CMD_V7_ERASE_AP 5 +#define CMD_V7_SENSOR_ID 6 + +#define v7_CMD_IDLE 0 +#define v7_CMD_WRITE_FW 1 +#define v7_CMD_WRITE_CONFIG 2 +#define v7_CMD_WRITE_LOCKDOWN 3 +#define v7_CMD_WRITE_GUEST_CODE 4 +#define v7_CMD_READ_CONFIG 5 +#define v7_CMD_ERASE_ALL 6 +#define v7_CMD_ERASE_UI_FIRMWARE 7 +#define v7_CMD_ERASE_UI_CONFIG 8 +#define v7_CMD_ERASE_BL_CONFIG 9 +#define v7_CMD_ERASE_DISP_CONFIG 10 +#define v7_CMD_ERASE_FLASH_CONFIG 11 +#define v7_CMD_ERASE_GUEST_CODE 12 +#define v7_CMD_ENABLE_FLASH_PROG 13 + +#define v7_UI_CONFIG_AREA 0 +#define v7_PM_CONFIG_AREA 1 +#define v7_BL_CONFIG_AREA 2 +#define v7_DP_CONFIG_AREA 3 +#define v7_FLASH_CONFIG_AREA 4 + +/* F34 V7 partition IDs */ +#define BOOTLOADER_PARTITION 1 +#define DEVICE_CONFIG_PARTITION 2 +#define FLASH_CONFIG_PARTITION 3 +#define MANUFACTURING_BLOCK_PARTITION 4 +#define GUEST_SERIALIZATION_PARTITION 5 +#define GLOBAL_PARAMETERS_PARTITION 6 +#define CORE_CODE_PARTITION 7 +#define CORE_CONFIG_PARTITION 8 +#define GUEST_CODE_PARTITION 9 +#define DISPLAY_CONFIG_PARTITION 10 + +/* F34 V7 container IDs */ +#define TOP_LEVEL_CONTAINER 0 +#define UI_CONTAINER 1 +#define UI_CONFIG_CONTAINER 2 +#define BL_CONTAINER 3 +#define BL_IMAGE_CONTAINER 4 +#define BL_CONFIG_CONTAINER 5 +#define BL_LOCKDOWN_INFO_CONTAINER 6 +#define PERMANENT_CONFIG_CONTAINER 7 +#define GUEST_CODE_CONTAINER 8 +#define BL_PROTOCOL_DESCRIPTOR_CONTAINER 9 +#define UI_PROTOCOL_DESCRIPTOR_CONTAINER 10 +#define RMI_SELF_DISCOVERY_CONTAINER 11 +#define RMI_PAGE_CONTENT_CONTAINER 12 +#define GENERAL_INFORMATION_CONTAINER 13 +#define DEVICE_CONFIG_CONTAINER 14 +#define FLASH_CONFIG_CONTAINER 15 +#define GUEST_SERIALIZATION_CONTAINER 16 +#define GLOBAL_PARAMETERS_CONTAINER 17 +#define CORE_CODE_CONTAINER 18 +#define CORE_CONFIG_CONTAINER 19 +#define DISPLAY_CONFIG_CONTAINER 20 + +struct f34v7_query_1_7 { + u8 bl_minor_revision; /* query 1 */ + u8 bl_major_revision; + __le32 bl_fw_id; /* query 2 */ + u8 minimum_write_size; /* query 3 */ + __le16 block_size; + __le16 flash_page_size; + __le16 adjustable_partition_area_size; /* query 4 */ + __le16 flash_config_length; /* query 5 */ + __le16 payload_length; /* query 6 */ + u8 partition_support[4]; /* query 7 */ +} __packed; + +struct f34v7_data_1_5 { + u8 partition_id; + __le16 block_offset; + __le16 transfer_length; + u8 command; + u8 payload[2]; +} __packed; + +struct block_data { + const void *data; + int size; +}; + +struct partition_table { + u8 partition_id; + u8 byte_1_reserved; + __le16 partition_length; + __le16 start_physical_address; + __le16 partition_properties; +} __packed; + +struct physical_address { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 guest_code; +}; + +struct container_descriptor { + __le32 content_checksum; + __le16 container_id; + u8 minor_version; + u8 major_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + u8 container_option_flags[4]; + __le32 content_options_length; + __le32 content_options_address; + __le32 content_length; + __le32 content_address; +} __packed; + +struct block_count { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 fl_config; + u16 pm_config; + u16 bl_config; + u16 lockdown; + u16 guest_code; +}; + +struct image_header_10 { + __le32 checksum; + u8 reserved_04; + u8 reserved_05; + u8 minor_header_version; + u8 major_header_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + __le32 top_level_container_start_addr; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_display_cfg; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int display_cfg_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +struct register_offset { + u8 properties; + u8 properties_2; + u8 block_size; + u8 block_count; + u8 gc_block_count; + u8 flash_status; + u8 partition_id; + u8 block_number; + u8 transfer_length; + u8 flash_cmd; + u8 payload; +}; + struct rmi_f34_firmware { __le32 checksum; u8 pad1[3]; @@ -56,13 +266,49 @@ struct f34v5_data { struct mutex flash_mutex; }; +struct f34v7_data { + bool has_display_cfg; + bool has_guest_code; + bool force_update; + bool in_bl_mode; + u8 *read_config_buf; + size_t read_config_buf_size; + u8 command; + u8 flash_status; + u16 block_size; + u16 config_block_count; + u16 config_size; + u16 config_area; + u16 flash_config_length; + u16 payload_length; + u8 partitions; + u16 partition_table_bytes; + bool new_partition_table; + + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct image_metadata img; + + const void *config_data; + const void *image; +}; + struct f34_data { struct rmi_function *fn; + u8 bl_version; unsigned char bootloader_id[5]; - unsigned char configuration_id[9]; + unsigned char configuration_id[CONFIG_ID_SIZE*2 + 1]; - struct f34v5_data v5; + union { + struct f34v5_data v5; + struct f34v7_data v7; + }; }; +int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw); +int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw); +int rmi_f34v7_probe(struct f34_data *f34); + #endif /* _RMI_F34_H */ diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c new file mode 100644 index 000000000000..ca31f9539d9b --- /dev/null +++ b/drivers/input/rmi4/rmi_f34v7.c @@ -0,0 +1,1372 @@ +/* + * Copyright (c) 2016, Zodiac Inflight Innovations + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include + +#include "rmi_driver.h" +#include "rmi_f34.h" + +static int rmi_f34v7_read_flash_status(struct f34_data *f34) +{ + u8 status; + u8 command; + int ret; + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.data_base_addr + f34->v7.off.flash_status, + &status, + sizeof(status)); + if (ret < 0) { + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Failed to read flash status\n", __func__); + return ret; + } + + f34->v7.in_bl_mode = status >> 7; + f34->v7.flash_status = status & 0x1f; + + if (f34->v7.flash_status != 0x00) { + dev_err(&f34->fn->dev, "%s: status=%d, command=0x%02x\n", + __func__, f34->v7.flash_status, f34->v7.command); + } + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd, + &command, + sizeof(command)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read flash command\n", + __func__); + return ret; + } + + f34->v7.command = command; + + return 0; +} + +static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + + rmi_f34v7_read_flash_status(f34); + + if ((f34->v7.command == v7_CMD_IDLE) + && (f34->v7.flash_status == 0x00)) { + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "Idle status detected\n"); + return 0; + } + } while (count < timeout_count); + + dev_err(&f34->fn->dev, + "%s: Timed out waiting for idle status\n", __func__); + + return -ETIMEDOUT; +} + +static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34, + u8 cmd) +{ + int ret; + u8 base; + struct f34v7_data_1_5 data_1_5; + + base = f34->fn->fd.data_base_addr; + + memset(&data_1_5, 0, sizeof(data_1_5)); + + switch (cmd) { + case v7_CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case v7_CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + } + + data_1_5.payload[0] = f34->bootloader_id[0]; + data_1_5.payload[1] = f34->bootloader_id[1]; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.partition_id, + &data_1_5, sizeof(data_1_5)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to write single transaction command\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_write_command(struct f34_data *f34, u8 cmd) +{ + int ret; + u8 base; + u8 command; + + base = f34->fn->fd.data_base_addr; + + switch (cmd) { + case v7_CMD_WRITE_FW: + case v7_CMD_WRITE_CONFIG: + case v7_CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case v7_CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case v7_CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case v7_CMD_ERASE_UI_FIRMWARE: + case v7_CMD_ERASE_BL_CONFIG: + case v7_CMD_ERASE_UI_CONFIG: + case v7_CMD_ERASE_DISP_CONFIG: + case v7_CMD_ERASE_FLASH_CONFIG: + case v7_CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case v7_CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + f34->v7.command = command; + + switch (cmd) { + case v7_CMD_ERASE_ALL: + case v7_CMD_ERASE_UI_FIRMWARE: + case v7_CMD_ERASE_BL_CONFIG: + case v7_CMD_ERASE_UI_CONFIG: + case v7_CMD_ERASE_DISP_CONFIG: + case v7_CMD_ERASE_FLASH_CONFIG: + case v7_CMD_ERASE_GUEST_CODE: + case v7_CMD_ENABLE_FLASH_PROG: + ret = rmi_f34v7_write_command_single_transaction(f34, cmd); + if (ret < 0) + return ret; + else + return 0; + default: + break; + } + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: writing cmd %02X\n", + __func__, command); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.flash_cmd, + &command, sizeof(command)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write flash command\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_write_partition_id(struct f34_data *f34, u8 cmd) +{ + int ret; + u8 base; + u8 partition; + + base = f34->fn->fd.data_base_addr; + + switch (cmd) { + case v7_CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case v7_CMD_WRITE_CONFIG: + case v7_CMD_READ_CONFIG: + if (f34->v7.config_area == v7_UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (f34->v7.config_area == v7_DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (f34->v7.config_area == v7_PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (f34->v7.config_area == v7_BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (f34->v7.config_area == v7_FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case v7_CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case v7_CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case v7_CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case v7_CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case v7_CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.partition_id, + &partition, sizeof(partition)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34) +{ + int ret; + u8 base; + __le16 length; + u16 block_number = 0; + + base = f34->fn->fd.data_base_addr; + + f34->v7.config_area = v7_FLASH_CONFIG_AREA; + + ret = rmi_f34v7_write_partition_id(f34, v7_CMD_READ_CONFIG); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + put_unaligned_le16(f34->v7.flash_config_length, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n", + __func__); + return ret; + } + + ret = rmi_f34v7_write_command(f34, v7_CMD_READ_CONFIG); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write command\n", + __func__); + return ret; + } + + ret = rmi_f34v7_wait_for_idle(f34, WRITE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to wait for idle status\n", + __func__); + return ret; + } + + ret = rmi_read_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + f34->v7.read_config_buf, + f34->v7.partition_table_bytes); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read block data\n", + __func__); + return ret; + } + + return 0; +} + +static void rmi_f34v7_parse_partition_table(struct f34_data *f34, + const void *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + int i; + int index; + u16 partition_length; + u16 physical_address; + const struct partition_table *ptable; + + for (i = 0; i < f34->v7.partitions; i++) { + index = i * 8 + 2; + ptable = partition_table + index; + partition_length = le16_to_cpu(ptable->partition_length); + physical_address = le16_to_cpu(ptable->start_physical_address); + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Partition entry %d: %*ph\n", + __func__, i, sizeof(struct partition_table), ptable); + switch (ptable->partition_id & 0x1f) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + } + } +} + +static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34) +{ + int ret; + u8 base; + int offset; + u8 query_0; + struct f34v7_query_1_7 query_1_7; + + base = f34->fn->fd.query_base_addr; + + ret = rmi_read_block(f34->fn->rmi_dev, + base, + &query_0, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x7) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n", + __func__); + return ret; + } + + f34->bootloader_id[0] = query_1_7.bl_minor_revision; + f34->bootloader_id[1] = query_1_7.bl_major_revision; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Bootloader V%d.%d\n", + f34->bootloader_id[1], f34->bootloader_id[0]); + + return 0; +} + +static int rmi_f34v7_read_queries(struct f34_data *f34) +{ + int ret; + int i, j; + u8 base; + int offset; + u8 *ptable; + u8 query_0; + struct f34v7_query_1_7 query_1_7; + + base = f34->fn->fd.query_base_addr; + + ret = rmi_read_block(f34->fn->rmi_dev, + base, + &query_0, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x07) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n", + __func__); + return ret; + } + + f34->bootloader_id[0] = query_1_7.bl_minor_revision; + f34->bootloader_id[1] = query_1_7.bl_major_revision; + + f34->v7.block_size = le16_to_cpu(query_1_7.block_size); + f34->v7.flash_config_length = + le16_to_cpu(query_1_7.flash_config_length); + f34->v7.payload_length = le16_to_cpu(query_1_7.payload_length); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n", + __func__, f34->v7.block_size); + + f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET; + f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET; + f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET; + f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + f34->v7.off.flash_cmd = V7_COMMAND_OFFSET; + f34->v7.off.payload = V7_PAYLOAD_OFFSET; + + f34->v7.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG; + f34->v7.has_guest_code = + query_1_7.partition_support[1] & HAS_GUEST_CODE; + + if (query_0 & HAS_CONFIG_ID) { + char f34_ctrl[CONFIG_ID_SIZE]; + int i = 0; + u8 *p = f34->configuration_id; + *p = '\0'; + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.control_base_addr, + f34_ctrl, + sizeof(f34_ctrl)); + if (ret) + return ret; + + /* Eat leading zeros */ + while (i < sizeof(f34_ctrl) && !f34_ctrl[i]) + i++; + + for (; i < sizeof(f34_ctrl); i++) + p += snprintf(p, f34->configuration_id + + sizeof(f34->configuration_id) - p, + "%02X", f34_ctrl[i]); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n", + f34->configuration_id); + } + + f34->v7.partitions = 0; + for (i = 0; i < sizeof(query_1_7.partition_support); i++) + for (j = 0; j < 8; j++) + if (query_1_7.partition_support[i] & (1 << j)) + f34->v7.partitions++; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: Supported partitions: %*ph\n", + __func__, sizeof(query_1_7.partition_support), + query_1_7.partition_support); + + + f34->v7.partition_table_bytes = f34->v7.partitions * 8 + 2; + + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, + f34->v7.partition_table_bytes, + GFP_KERNEL); + if (!f34->v7.read_config_buf) { + f34->v7.read_config_buf_size = 0; + return -ENOMEM; + } + + f34->v7.read_config_buf_size = f34->v7.partition_table_bytes; + ptable = f34->v7.read_config_buf; + + ret = rmi_f34v7_read_f34v7_partition_table(f34); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read partition table\n", + __func__); + return ret; + } + + rmi_f34v7_parse_partition_table(f34, ptable, + &f34->v7.blkcount, &f34->v7.phyaddr); + + return 0; +} + +static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.ui_firmware) { + dev_err(&f34->fn->dev, + "UI firmware size mismatch: %d != %d\n", + block_count, f34->v7.blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_ui_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.ui_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.ui_config) { + dev_err(&f34->fn->dev, "UI config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_dp_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.dp_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.dp_config) { + dev_err(&f34->fn->dev, "Display config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_guest_code_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.guest_code.size / f34->v7.block_size; + if (block_count != f34->v7.blkcount.guest_code) { + dev_err(&f34->fn->dev, "Guest code size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_bl_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.bl_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.bl_config) { + dev_err(&f34->fn->dev, "Bootloader config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_erase_config(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing config...\n"); + + switch (f34->v7.config_area) { + case v7_UI_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG); + if (ret < 0) + return ret; + break; + case v7_DP_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG); + if (ret < 0) + return ret; + break; + case v7_BL_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG); + if (ret < 0) + return ret; + break; + } + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + return ret; +} + +static int rmi_f34v7_erase_guest_code(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing guest code...\n"); + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_erase_all(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing firmware...\n"); + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + f34->v7.config_area = v7_UI_CONFIG_AREA; + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + + if (f34->v7.has_display_cfg) { + f34->v7.config_area = v7_DP_CONFIG_AREA; + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + } + + if (f34->v7.new_partition_table && f34->v7.has_guest_code) { + ret = rmi_f34v7_erase_guest_code(f34); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 block_number = 0; + u16 index = 0; + + base = f34->fn->fd.data_base_addr; + + ret = rmi_f34v7_write_partition_id(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + max_transfer = min(f34->v7.payload_length, + (u16)(PAGE_SIZE / f34->v7.block_size)); + + do { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Wait for idle failed (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_read_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + &f34->v7.read_config_buf[index], + transfer * f34->v7.block_size); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Read block failed (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + index += (transfer * f34->v7.block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34, + const void *block_ptr, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 block_number = 0; + + base = f34->fn->fd.data_base_addr; + + ret = rmi_f34v7_write_partition_id(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size)) + max_transfer = PAGE_SIZE / f34->v7.block_size; + else + max_transfer = f34->v7.payload_length; + + do { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + block_ptr, transfer * f34->v7.block_size); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed writing data (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed wait for idle (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + block_ptr += (transfer * f34->v7.block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int rmi_f34v7_write_config(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.config_data, + f34->v7.config_block_count, + v7_CMD_WRITE_CONFIG); +} + +static int rmi_f34v7_write_ui_config(struct f34_data *f34) +{ + f34->v7.config_area = v7_UI_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.ui_config.data; + f34->v7.config_size = f34->v7.img.ui_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + return rmi_f34v7_write_config(f34); +} + +static int rmi_f34v7_write_dp_config(struct f34_data *f34) +{ + f34->v7.config_area = v7_DP_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.dp_config.data; + f34->v7.config_size = f34->v7.img.dp_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + return rmi_f34v7_write_config(f34); +} + +static int rmi_f34v7_write_guest_code(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.guest_code.data, + f34->v7.img.guest_code.size / + f34->v7.block_size, + v7_CMD_WRITE_GUEST_CODE); +} + +static int rmi_f34v7_write_flash_config(struct f34_data *f34) +{ + int ret; + + f34->v7.config_area = v7_FLASH_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.fl_config.data; + f34->v7.config_size = f34->v7.img.fl_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + if (f34->v7.config_block_count != f34->v7.blkcount.fl_config) { + dev_err(&f34->fn->dev, "%s: Flash config size mismatch\n", + __func__); + return -EINVAL; + } + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG); + if (ret < 0) + return ret; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Erase flash config command written\n", __func__); + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_partition_table(struct f34_data *f34) +{ + u16 block_count; + int ret; + + block_count = f34->v7.blkcount.bl_config; + f34->v7.config_area = v7_BL_CONFIG_AREA; + f34->v7.config_size = f34->v7.block_size * block_count; + devm_kfree(&f34->fn->dev, f34->v7.read_config_buf); + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, + f34->v7.config_size, GFP_KERNEL); + if (!f34->v7.read_config_buf) { + f34->v7.read_config_buf_size = 0; + return -ENOMEM; + } + + f34->v7.read_config_buf_size = f34->v7.config_size; + + ret = rmi_f34v7_read_f34v7_blocks(f34, block_count, v7_CMD_READ_CONFIG); + if (ret < 0) + return ret; + + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_flash_config(f34); + if (ret < 0) + return ret; + + f34->v7.config_area = v7_BL_CONFIG_AREA; + f34->v7.config_data = f34->v7.read_config_buf; + f34->v7.config_size = f34->v7.img.bl_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + ret = rmi_f34v7_write_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_firmware(struct f34_data *f34) +{ + u16 blk_count; + + blk_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.ui_firmware.data, + blk_count, v7_CMD_WRITE_FW); +} + +static void rmi_f34v7_compare_partition_tables(struct f34_data *f34) +{ + if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.has_display_cfg && + f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.has_guest_code && + f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) { + f34->v7.new_partition_table = true; + return; + } + + f34->v7.new_partition_table = false; +} + +static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34, + const void *image) +{ + int i; + int num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const void *content; + const struct container_descriptor *descriptor; + + num_of_containers = f34->v7.img.bootloader.size / 4 - 1; + + for (i = 1; i <= num_of_containers; i++) { + addr = get_unaligned_le32(f34->v7.img.bootloader.data + i * 4); + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + f34->v7.img.bl_config.data = content; + f34->v7.img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + f34->v7.img.lockdown.data = content; + f34->v7.img.lockdown.size = length; + break; + default: + break; + } + } +} + +static void rmi_f34v7_parse_image_header_10(struct f34_data *f34) +{ + unsigned int i; + unsigned int num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const void *image = f34->v7.image; + const u8 *content; + const struct container_descriptor *descriptor; + const struct image_header_10 *header = image; + + f34->v7.img.checksum = le32_to_cpu(header->checksum); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.img.checksum=%X\n", + __func__, f34->v7.img.checksum); + + /* address of top level container */ + offset = le32_to_cpu(header->top_level_container_start_addr); + descriptor = image + offset; + + /* address of top level container content */ + offset = le32_to_cpu(descriptor->content_address); + num_of_containers = le32_to_cpu(descriptor->content_length) / 4; + + for (i = 0; i < num_of_containers; i++) { + addr = get_unaligned_le32(image + offset); + offset += 4; + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(descriptor->content_length); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: container_id=%d, length=%d\n", __func__, + container_id, length); + + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + f34->v7.img.ui_firmware.data = content; + f34->v7.img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + f34->v7.img.ui_config.data = content; + f34->v7.img.ui_config.size = length; + break; + case BL_CONTAINER: + f34->v7.img.bl_version = *content; + f34->v7.img.bootloader.data = content; + f34->v7.img.bootloader.size = length; + rmi_f34v7_parse_img_header_10_bl_container(f34, image); + break; + case GUEST_CODE_CONTAINER: + f34->v7.img.contains_guest_code = true; + f34->v7.img.guest_code.data = content; + f34->v7.img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + f34->v7.img.contains_display_cfg = true; + f34->v7.img.dp_config.data = content; + f34->v7.img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + f34->v7.img.contains_flash_config = true; + f34->v7.img.fl_config.data = content; + f34->v7.img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + f34->v7.img.contains_firmware_id = true; + f34->v7.img.firmware_id = + get_unaligned_le32(content + 4); + break; + default: + break; + } + } +} + +static int rmi_f34v7_parse_image_info(struct f34_data *f34) +{ + const struct image_header_10 *header = f34->v7.image; + + memset(&f34->v7.img, 0x00, sizeof(f34->v7.img)); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: header->major_header_version = %d\n", + __func__, header->major_header_version); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + rmi_f34v7_parse_image_header_10(f34); + break; + default: + dev_err(&f34->fn->dev, "Unsupported image file format %02X\n", + header->major_header_version); + return -EINVAL; + } + + if (!f34->v7.img.contains_flash_config) { + dev_err(&f34->fn->dev, "%s: No flash config in fw image\n", + __func__); + return -EINVAL; + } + + rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data, + &f34->v7.img.blkcount, &f34->v7.img.phyaddr); + + rmi_f34v7_compare_partition_tables(f34); + + return 0; +} + +int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw) +{ + int ret; + + rmi_f34v7_read_queries_bl_version(f34); + + f34->v7.image = fw->data; + + ret = rmi_f34v7_parse_image_info(f34); + if (ret < 0) + goto fail; + + if (!f34->v7.new_partition_table) { + ret = rmi_f34v7_check_ui_firmware_size(f34); + if (ret < 0) + goto fail; + + ret = rmi_f34v7_check_ui_config_size(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && + f34->v7.img.contains_display_cfg) { + ret = rmi_f34v7_check_dp_config_size(f34); + if (ret < 0) + goto fail; + } + + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) { + ret = rmi_f34v7_check_guest_code_size(f34); + if (ret < 0) + goto fail; + } + } else { + ret = rmi_f34v7_check_bl_config_size(f34); + if (ret < 0) + goto fail; + } + + ret = rmi_f34v7_erase_all(f34); + if (ret < 0) + goto fail; + + if (f34->v7.new_partition_table) { + ret = rmi_f34v7_write_partition_table(f34); + if (ret < 0) + goto fail; + dev_info(&f34->fn->dev, "%s: Partition table programmed\n", + __func__); + } + + dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n", + f34->v7.img.ui_firmware.size); + + ret = rmi_f34v7_write_firmware(f34); + if (ret < 0) + goto fail; + + dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n", + f34->v7.img.ui_config.size); + + f34->v7.config_area = v7_UI_CONFIG_AREA; + ret = rmi_f34v7_write_ui_config(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) { + dev_info(&f34->fn->dev, "Writing display config...\n"); + + ret = rmi_f34v7_write_dp_config(f34); + if (ret < 0) + goto fail; + } + + if (f34->v7.new_partition_table) { + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) { + dev_info(&f34->fn->dev, "Writing guest code...\n"); + + ret = rmi_f34v7_write_guest_code(f34); + if (ret < 0) + goto fail; + } + } + +fail: + return ret; +} + +static int rmi_f34v7_enter_flash_prog(struct f34_data *f34) +{ + int ret; + + ret = rmi_f34v7_read_flash_status(f34); + if (ret < 0) + return ret; + + if (f34->v7.in_bl_mode) + return 0; + + ret = rmi_f34v7_write_command(f34, v7_CMD_ENABLE_FLASH_PROG); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + if (!f34->v7.in_bl_mode) { + dev_err(&f34->fn->dev, "%s: BL mode not entered\n", __func__); + return -EINVAL; + } + + return 0; +} + +int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw) +{ + int ret = 0; + + f34->v7.config_area = v7_UI_CONFIG_AREA; + f34->v7.image = fw->data; + + ret = rmi_f34v7_parse_image_info(f34); + if (ret < 0) + goto exit; + + if (!f34->v7.force_update && f34->v7.new_partition_table) { + dev_err(&f34->fn->dev, "%s: Partition table mismatch\n", + __func__); + ret = -EINVAL; + goto exit; + } + + dev_info(&f34->fn->dev, "Firmware image OK\n"); + + ret = rmi_f34v7_read_flash_status(f34); + if (ret < 0) + goto exit; + + if (f34->v7.in_bl_mode) { + dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n", + __func__); + } + + rmi_f34v7_enter_flash_prog(f34); + + return 0; + +exit: + return ret; +} + +int rmi_f34v7_probe(struct f34_data *f34) +{ + int ret; + + /* Read bootloader version */ + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.query_base_addr + V7_BOOTLOADER_ID_OFFSET, + f34->bootloader_id, + sizeof(f34->bootloader_id)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read bootloader ID\n", + __func__); + return ret; + } + + if (f34->bootloader_id[1] == '5') { + f34->bl_version = 5; + } else if (f34->bootloader_id[1] == '6') { + f34->bl_version = 6; + } else if (f34->bootloader_id[1] == 7) { + f34->bl_version = 7; + } else { + dev_err(&f34->fn->dev, "%s: Unrecognized bootloader version\n", + __func__); + return -EINVAL; + } + + memset(&f34->v7.blkcount, 0x00, sizeof(f34->v7.blkcount)); + memset(&f34->v7.phyaddr, 0x00, sizeof(f34->v7.phyaddr)); + rmi_f34v7_read_queries(f34); + + f34->v7.force_update = false; + return 0; +} diff --git a/include/linux/rmi.h b/include/linux/rmi.h index ac910f730688..64125443f8a6 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -342,7 +342,7 @@ struct rmi_driver_data { struct rmi_function *f01_container; struct rmi_function *f34_container; - bool f01_bootloader_mode; + bool bootloader_mode; int num_of_irq_regs; int irq_count; -- cgit v1.2.3 From 34888602eb99df174e76eafeb613ad857f534ebb Mon Sep 17 00:00:00 2001 From: Jingkui Wang Date: Sat, 10 Dec 2016 22:44:10 -0800 Subject: Input: drv260x - use generic device properties Update driver drv260x to use generic device properties so that it can be used on non-DT systems. We also remove platform data as generic device properties work on static board code as well. Signed-off-by: Jingkui Wang Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 83 +++++++---------------------- include/linux/platform_data/drv260x-pdata.h | 28 ---------- 2 files changed, 19 insertions(+), 92 deletions(-) delete mode 100644 include/linux/platform_data/drv260x-pdata.h (limited to 'include') diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index a26aa548dd66..0a2b865b1000 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -27,7 +25,6 @@ #include #include -#include #define DRV260X_STATUS 0x0 #define DRV260X_MODE 0x1 @@ -468,86 +465,36 @@ static const struct regmap_config drv260x_regmap_config = { .cache_type = REGCACHE_NONE, }; -#ifdef CONFIG_OF -static int drv260x_parse_dt(struct device *dev, - struct drv260x_data *haptics) -{ - struct device_node *np = dev->of_node; - unsigned int voltage; - int error; - - error = of_property_read_u32(np, "mode", &haptics->mode); - if (error) { - dev_err(dev, "%s: No entry for mode\n", __func__); - return error; - } - - error = of_property_read_u32(np, "library-sel", &haptics->library); - if (error) { - dev_err(dev, "%s: No entry for library selection\n", - __func__); - return error; - } - - error = of_property_read_u32(np, "vib-rated-mv", &voltage); - if (!error) - haptics->rated_voltage = drv260x_calculate_voltage(voltage); - - - error = of_property_read_u32(np, "vib-overdrive-mv", &voltage); - if (!error) - haptics->overdrive_voltage = drv260x_calculate_voltage(voltage); - - return 0; -} -#else -static inline int drv260x_parse_dt(struct device *dev, - struct drv260x_data *haptics) -{ - dev_err(dev, "no platform data defined\n"); - - return -EINVAL; -} -#endif - static int drv260x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; struct drv260x_data *haptics; + u32 voltage; int error; haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL); if (!haptics) return -ENOMEM; - haptics->overdrive_voltage = DRV260X_DEF_OD_CLAMP_VOLT; - haptics->rated_voltage = DRV260X_DEF_RATED_VOLT; - - if (pdata) { - haptics->mode = pdata->mode; - haptics->library = pdata->library_selection; - if (pdata->vib_overdrive_voltage) - haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage); - if (pdata->vib_rated_voltage) - haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage); - } else if (client->dev.of_node) { - error = drv260x_parse_dt(&client->dev, haptics); - if (error) - return error; - } else { - dev_err(dev, "Platform data not set\n"); - return -ENODEV; + error = device_property_read_u32(dev, "mode", &haptics->mode); + if (error) { + dev_err(dev, "Can't fetch 'mode' property: %d\n", error); + return error; } - if (haptics->mode < DRV260X_LRA_MODE || haptics->mode > DRV260X_ERM_MODE) { dev_err(dev, "Vibrator mode is invalid: %i\n", haptics->mode); return -EINVAL; } + error = device_property_read_u32(dev, "library-sel", &haptics->library); + if (error) { + dev_err(dev, "Can't fetch 'library-sel' property: %d\n", error); + return error; + } + if (haptics->library < DRV260X_LIB_EMPTY || haptics->library > DRV260X_ERM_LIB_F) { dev_err(dev, @@ -569,6 +516,14 @@ static int drv260x_probe(struct i2c_client *client, return -EINVAL; } + error = device_property_read_u32(dev, "vib-rated-mv", &voltage); + haptics->rated_voltage = error ? DRV260X_DEF_RATED_VOLT : + drv260x_calculate_voltage(voltage); + + error = device_property_read_u32(dev, "vib-overdrive-mv", &voltage); + haptics->overdrive_voltage = error ? DRV260X_DEF_OD_CLAMP_VOLT : + drv260x_calculate_voltage(voltage); + haptics->regulator = devm_regulator_get(dev, "vbat"); if (IS_ERR(haptics->regulator)) { error = PTR_ERR(haptics->regulator); diff --git a/include/linux/platform_data/drv260x-pdata.h b/include/linux/platform_data/drv260x-pdata.h deleted file mode 100644 index 0a03b0944411..000000000000 --- a/include/linux/platform_data/drv260x-pdata.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Platform data for DRV260X haptics driver family - * - * Author: Dan Murphy - * - * Copyright: (C) 2014 Texas Instruments, Inc. - * - * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#ifndef _LINUX_DRV260X_PDATA_H -#define _LINUX_DRV260X_PDATA_H - -struct drv260x_platform_data { - u32 library_selection; - u32 mode; - u32 vib_rated_voltage; - u32 vib_overdrive_voltage; -}; - -#endif -- cgit v1.2.3