diff mbox

[05/05] input synaptics-rmi4: Add reflash support

Message ID 1394245795-17347-5-git-send-email-cheiny@synaptics.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christopher Heiny March 8, 2014, 2:29 a.m. UTC
Add support for reflashing V5 bootloader firmwares into 
RMI4 devices.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---

 drivers/input/rmi4/Kconfig         |   9 +
 drivers/input/rmi4/Makefile        |   1 +
 drivers/input/rmi4/rmi_bus.c       |   3 +
 drivers/input/rmi4/rmi_driver.h    |  11 +
 drivers/input/rmi4/rmi_fw_update.c | 961 +++++++++++++++++++++++++++++++++++++
 5 files changed, 985 insertions(+)

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Courtney Cavin March 10, 2014, 4:24 p.m. UTC | #1
On Sat, Mar 08, 2014 at 03:29:55AM +0100, Christopher Heiny wrote:
> Add support for reflashing V5 bootloader firmwares into
> RMI4 devices.

Throughout this driver: I'm not sure of the name 'reflash'. Maybe just
'flash(ing)'?

> 
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> Cc: Linux Walleij <linus.walleij@linaro.org>
> Cc: David Herrmann <dh.herrmann@gmail.com>
> Cc: Jiri Kosina <jkosina@suse.cz>
> 
> ---
> 
>  drivers/input/rmi4/Kconfig         |   9 +
>  drivers/input/rmi4/Makefile        |   1 +
>  drivers/input/rmi4/rmi_bus.c       |   3 +
>  drivers/input/rmi4/rmi_driver.h    |  11 +
>  drivers/input/rmi4/rmi_fw_update.c | 961 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 985 insertions(+)
> 
> diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
> index d0c7b6e..9b88b6a 100644
> --- a/drivers/input/rmi4/Kconfig
> +++ b/drivers/input/rmi4/Kconfig
> @@ -25,6 +25,15 @@ config RMI4_DEBUG
> 
>           If unsure, say N.
> 
> +config RMI4_FWLIB

Err. LIB?

> +       bool "RMI4 Firmware Update"
> +       depends on RMI4_CORE
> +       help
> +         Say Y here to enable in-kernel firmware update capability.
> +
> +         Add support to the RMI4 core to enable reflashing of device
> +         firmware.

Please provide more description here of what someone is supposed to do
to utilize this support, and what it actually does.  The term "update"
here is generic enough to cause some confusion. 

> +
>  config RMI4_I2C
>         tristate "RMI4 I2C Support"
>         depends on RMI4_CORE && I2C
> diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
> index 5c6bad5..570ea80 100644
> --- a/drivers/input/rmi4/Makefile
> +++ b/drivers/input/rmi4/Makefile
> @@ -1,5 +1,6 @@
>  obj-$(CONFIG_RMI4_CORE) += rmi_core.o
>  rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
> +rmi_core-$(CONFIG_RMI4_FWLIB) += rmi_fw_update.o

Ok.  Now I'm thoroughly confused, and I haven't even gotten to the code
yet.  FWLIB => rmi_fw_update => rmi_reflash.  What are we doing again?

> 
>  # Function drivers
>  obj-$(CONFIG_RMI4_F11) += rmi_f11.o
> diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
> index 6e0454a..3c93d08 100644
> --- a/drivers/input/rmi4/rmi_bus.c
> +++ b/drivers/input/rmi4/rmi_bus.c
> @@ -117,6 +117,8 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport)
>         if (error)
>                 goto err_put_device;
> 
> +       rmi_reflash_init(rmi_dev);
> +
>         dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
>                 pdata->sensor_name, dev_name(&rmi_dev->dev));
> 
> @@ -139,6 +141,7 @@ void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
>         struct rmi_device *rmi_dev = xport->rmi_dev;
> 
>         device_del(&rmi_dev->dev);
> +       rmi_reflash_cleanup(rmi_dev);
>         rmi_physical_teardown_debugfs(rmi_dev);
>         put_device(&rmi_dev->dev);
>  }
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
[...]
> +#ifdef CONFIG_RMI4_FWLIB
> +void rmi_reflash_init(struct rmi_device *rmi_dev);
> +void rmi_reflash_cleanup(struct rmi_device *rmi_dev);
> +#else
> +#define rmi_reflash_init(rmi_dev)
> +#define rmi_reflash_cleanup(rmi_dev)

Please use static inline functions here.  It helps the compiler tell
you when you do something wrong.

> +#endif
> 
>  #endif
> diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
[...]
> +struct rmi_reflash_data {
> +       struct rmi_device *rmi_dev;
> +       bool force;
> +       ulong busy;
> +       char name_buf[RMI_NAME_BUFFER_SIZE];
> +       char *img_name;

const?

> +       struct pdt_entry f01_pdt;
> +       struct f01_basic_properties f01_props;
> +       u8 device_status;
> +       struct pdt_entry f34_pdt;
> +       u8 bootloader_id[2];
> +       struct rmi_f34_queries f34_queries;
> +       u16 f34_status_address;
> +       struct rmi_f34_control_status f34_controls;
> +       const u8 *firmware_data;
> +       const u8 *config_data;
> +       struct work_struct reflash_work;
> +};
[...]
> +static int rmi_read_f01_status(struct rmi_reflash_data *data)
> +{
> +       int retval;
> +
> +       retval = rmi_read(data->rmi_dev, data->f01_pdt.data_base_addr,
> +                         &data->device_status);
> +       if (retval)
> +               return retval;
> +
> +       return 0;
> +}

This function is used one time, just calls rmi_read... There's no need
to wrap this read.

[...]
> +static int rmi_wait_for_idle(struct rmi_reflash_data *data, int timeout_ms)
> +{
> +       int timeout_count = ((timeout_ms * 1000) / RMI_MAX_SLEEP_TIME_US) + 1;
> +       int count = 0;
> +       struct rmi_f34_control_status *controls = &data->f34_controls;
> +       int retval;
> +
> +       do {
> +               if (count || timeout_count == 1)
> +                       usleep_range(RMI_MIN_SLEEP_TIME_US,
> +                                    RMI_MAX_SLEEP_TIME_US);
> +               retval = rmi_read_f34_controls(data);
> +               count++;
> +               if (retval)
> +                       continue;
> +               else if (IS_IDLE(controls)) {
> +                       if (dev_WARN_ONCE(&data->rmi_dev->dev,
> +                                         !data->f34_controls.program_enabled,
> +                                         "Reflash is idle but program_enabled bit isn't set.\n"
> +                                         ))
> +                               /*
> +                                * This works around a bug in certain device
> +                                * firmwares, where the idle state is reached,
> +                                * but the program_enabled bit is not yet set.
> +                                */

If it's a bug in devices, but it's ok to try again as a workaround, is
there a good reason to print a stacktrace?  Does the user care?

> +                               continue;
> +                       return 0;
> +               }
> +       } while (count < timeout_count);
> +
> +       dev_err(&data->rmi_dev->dev,
> +               "ERROR: Timeout waiting for idle status.\n");
> +       dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
> +       dev_err(&data->rmi_dev->dev, "Status:  %#04x\n", controls->status);
> +       dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
> +                       controls->program_enabled);
> +       dev_err(&data->rmi_dev->dev, "Idle:    %d\n", IS_IDLE(controls));
> +       return -ETIMEDOUT;
> +}
[...]
> +static int rmi_write_f34_command(struct rmi_reflash_data *data, u8 command)
> +{
> +       int retval;
> +       struct rmi_device *rmi_dev = data->rmi_dev;
> +
> +       retval = rmi_write(rmi_dev, data->f34_status_address, command);
> +       if (retval < 0) {
> +               dev_err(&rmi_dev->dev,
> +                       "Failed to write F34 command %#04x. Code: %d.\n",
> +                       command, retval);
> +               return retval;
> +       }
> +
> +       return 0;
> +}

This function is used three times, and calls one function without any
wrapping magic.  You could easily call rmi_write in each place.

[...]
> +static void rmi_reset_device(struct rmi_reflash_data *data)

It really feels like this should have an error return.

> +{
> +       int retval;
> +       const struct rmi_device_platform_data *pdata =
> +                               rmi_get_platform_data(data->rmi_dev);
> +
> +       dev_dbg(&data->rmi_dev->dev, "Resetting...\n");
> +       retval = rmi_write(data->rmi_dev, data->f01_pdt.command_base_addr,
> +                          RMI_F01_CMD_DEVICE_RESET);
> +       if (retval < 0)
> +               dev_warn(&data->rmi_dev->dev,
> +                        "WARNING - post-flash reset failed, code: %d.\n",
> +                        retval);
> +       msleep(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
> +       dev_dbg(&data->rmi_dev->dev, "Reset completed.\n");
> +}

> +static int rmi_write_firmware(struct rmi_reflash_data *data)
> +{
> +       return rmi_write_blocks(data, (u8 *) data->firmware_data,
> +               data->f34_queries.fw_block_count, RMI_F34_WRITE_FW_BLOCK);
> +}

Called once.

> +
> +static int rmi_write_configuration(struct rmi_reflash_data *data)
> +{
> +       return rmi_write_blocks(data, (u8 *) data->config_data,
> +               data->f34_queries.config_block_count,
> +               RMI_F34_WRITE_CONFIG_BLOCK);
> +}

Called once.

> +
> +static void rmi_reflash_firmware(struct rmi_reflash_data *data)

Also feels like this should have an error return.

[...]
> +static void rmi_fw_update(struct rmi_device *rmi_dev)

Same.

> +{
[...]
> +       retval = rmi_read_f34_queries(data);
> +       if (retval) {
> +               dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
> +                       retval);
> +               goto done;
> +       }
> +       if (data->img_name && strlen(data->img_name))

data->img_name && data->img_name[0] ?  No need to waste extra cycles.

> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
> +                        rmi_fw_name_format, data->img_name);
> +       else if (pdata->firmware_name && strlen(pdata->firmware_name))

Same.

> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
> +                        rmi_fw_name_format, pdata->firmware_name);
> +       else {
> +               if (!strlen(data->f01_props.product_id)) {

Same.

> +                       dev_err(&rmi_dev->dev, "Product ID is missing or empty - will not reflash.\n");
> +                       goto done;
> +               }
> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
> +                        rmi_fw_name_format, data->f01_props.product_id);
> +       }
[...]
> +       if (rmi_go_nogo(data, header)) {
> +               dev_dbg(&rmi_dev->dev, "Go/NoGo said go.\n");
> +               rmi_free_function_list(rmi_dev);
> +               rmi_reflash_firmware(data);
> +               rmi_reset_device(data);
> +               rmi_driver_detect_functions(rmi_dev);
> +       } else
> +               dev_dbg(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");

Documentation/CodingStyle says:
} else {
	...
}

[...]
> +}
[...]
> +static ssize_t rmi_reflash_force_store(struct device *dev,
> +                                      struct device_attribute *attr,
> +                                      const char *buf, size_t count)
> +{
> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
> +       struct rmi_reflash_data *data = drv_data->reflash_data;
> +       int retval;
> +       unsigned long val;
> +
> +       if (test_and_set_bit(0, &data->busy))
> +               return -EBUSY;
> +
> +       retval = kstrtoul(buf, 10, &val);
> +       if (retval)
> +               count = retval;
> +       else
> +               data->force = !!val;

Hrm. Perhaps '42' doesn't make sense here.  Maybe add:

else if (val > 1)
	count = -EINVAL;

> +
> +       clear_bit(0, &data->busy);
> +
> +       return count;
> +}
> +
> +static ssize_t rmi_reflash_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
> +       struct rmi_reflash_data *data = drv_data->reflash_data;
> +
> +       return snprintf(buf, PAGE_SIZE, "%u\n", test_bit(0, &data->busy));
> +}
> +
> +static ssize_t rmi_reflash_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t count)
> +{
> +       int retval;
> +       unsigned long val;
> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
> +       struct rmi_reflash_data *data = drv_data->reflash_data;
> +
> +       retval = kstrtoul(buf, 10, &val);
> +       if (retval)
> +               return retval;
> +
> +       if (test_and_set_bit(0, &data->busy))
> +               return -EBUSY;
> +
> +       if (val)
> +               /*
> +                * TODO: Here we start a work thread to go do the reflash, but
> +                * maybe we can just use request_firmware_timeout().
> +                */

Mmm.. Yes.  It would make the lifecycle of this busy bit much more
obvious.  Otherwise perhaps call request_firmware_nowait() ?  It already
does this work queue ... uh ... work for you.

> +               schedule_work(&data->reflash_work);
> +       else
> +               clear_bit(0, &data->busy);
> +
> +       return count;
> +}
[...]
> +void rmi_reflash_init(struct rmi_device *rmi_dev)
> +{
> +       int error;
> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
> +       struct rmi_reflash_data *data;
> +
> +       dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
> +
> +       data = devm_kzalloc(&rmi_dev->dev, sizeof(struct rmi_reflash_data),
> +                           GFP_KERNEL);

The memory ownership here is odd.  Maybe kzalloc, and just return that
data?

> +
> +       error = sysfs_create_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
> +       if (error) {
> +               dev_warn(&rmi_dev->dev, "Failed to create reflash sysfs attributes.\n");
> +               return;
> +       }
> +
> +       INIT_WORK(&data->reflash_work, rmi_reflash_work);
> +       data->rmi_dev = rmi_dev;
> +       drv_data->reflash_data = data;
> +}
> +
> +void rmi_reflash_cleanup(struct rmi_device *rmi_dev)
> +{
> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
> +       struct rmi_reflash_data *data = drv_data->reflash_data;
> +
> +       sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
> +       devm_kfree(&rmi_dev->dev, data);

Who owns this memory again? devm_ doesn't seem right for this use-case.

> +}

-Courtney
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christopher Heiny March 11, 2014, 1:03 a.m. UTC | #2
On 03/10/2014 09:24 AM, Courtney Cavin wrote:
> On Sat, Mar 08, 2014 at 03:29:55AM +0100, Christopher Heiny wrote:
>> Add support for reflashing V5 bootloader firmwares into
>> RMI4 devices.
>
> Throughout this driver: I'm not sure of the name 'reflash'. Maybe just
> 'flash(ing)'?

That's a good point.  We use the terms 'flash', 'reflash', and 'update' 
pretty much interchangeably internally, and didn't realize it might 
cause confusion.  Using 'fw update' and similar terms would probably 
eliminate that.

>
>>
>> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
>> Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
>> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>> Cc: Linux Walleij <linus.walleij@linaro.org>
>> Cc: David Herrmann <dh.herrmann@gmail.com>
>> Cc: Jiri Kosina <jkosina@suse.cz>
>>
>> ---
>>
>>   drivers/input/rmi4/Kconfig         |   9 +
>>   drivers/input/rmi4/Makefile        |   1 +
>>   drivers/input/rmi4/rmi_bus.c       |   3 +
>>   drivers/input/rmi4/rmi_driver.h    |  11 +
>>   drivers/input/rmi4/rmi_fw_update.c | 961 +++++++++++++++++++++++++++++++++++++
>>   5 files changed, 985 insertions(+)
>>
>> diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
>> index d0c7b6e..9b88b6a 100644
>> --- a/drivers/input/rmi4/Kconfig
>> +++ b/drivers/input/rmi4/Kconfig
>> @@ -25,6 +25,15 @@ config RMI4_DEBUG
>>
>>            If unsure, say N.
>>
>> +config RMI4_FWLIB
>
> Err. LIB?

It's an evolutionary vestige - originally this was a library module. 
I'll change it as part of the change mentioned above.

>
>> +       bool "RMI4 Firmware Update"
>> +       depends on RMI4_CORE
>> +       help
>> +         Say Y here to enable in-kernel firmware update capability.
>> +
>> +         Add support to the RMI4 core to enable reflashing of device
>> +         firmware.
>
> Please provide more description here of what someone is supposed to do
> to utilize this support, and what it actually does.  The term "update"
> here is generic enough to cause some confusion.

OK

>
>> +
>>   config RMI4_I2C
>>          tristate "RMI4 I2C Support"
>>          depends on RMI4_CORE && I2C
>> diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
>> index 5c6bad5..570ea80 100644
>> --- a/drivers/input/rmi4/Makefile
>> +++ b/drivers/input/rmi4/Makefile
>> @@ -1,5 +1,6 @@
>>   obj-$(CONFIG_RMI4_CORE) += rmi_core.o
>>   rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
>> +rmi_core-$(CONFIG_RMI4_FWLIB) += rmi_fw_update.o
>
> Ok.  Now I'm thoroughly confused, and I haven't even gotten to the code
> yet.  FWLIB => rmi_fw_update => rmi_reflash.  What are we doing again?

See my first comment.

>
>>
>>   # Function drivers
>>   obj-$(CONFIG_RMI4_F11) += rmi_f11.o
>> diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
>> index 6e0454a..3c93d08 100644
>> --- a/drivers/input/rmi4/rmi_bus.c
>> +++ b/drivers/input/rmi4/rmi_bus.c
>> @@ -117,6 +117,8 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport)
>>          if (error)
>>                  goto err_put_device;
>>
>> +       rmi_reflash_init(rmi_dev);
>> +
>>          dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
>>                  pdata->sensor_name, dev_name(&rmi_dev->dev));
>>
>> @@ -139,6 +141,7 @@ void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
>>          struct rmi_device *rmi_dev = xport->rmi_dev;
>>
>>          device_del(&rmi_dev->dev);
>> +       rmi_reflash_cleanup(rmi_dev);
>>          rmi_physical_teardown_debugfs(rmi_dev);
>>          put_device(&rmi_dev->dev);
>>   }
>> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
> [...]
>> +#ifdef CONFIG_RMI4_FWLIB
>> +void rmi_reflash_init(struct rmi_device *rmi_dev);
>> +void rmi_reflash_cleanup(struct rmi_device *rmi_dev);
>> +#else
>> +#define rmi_reflash_init(rmi_dev)
>> +#define rmi_reflash_cleanup(rmi_dev)
>
> Please use static inline functions here.  It helps the compiler tell
> you when you do something wrong.

OK

>
>> +#endif
>>
>>   #endif
>> diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
> [...]
>> +struct rmi_reflash_data {
>> +       struct rmi_device *rmi_dev;
>> +       bool force;
>> +       ulong busy;
>> +       char name_buf[RMI_NAME_BUFFER_SIZE];
>> +       char *img_name;
>
> const?

OK

>
>> +       struct pdt_entry f01_pdt;
>> +       struct f01_basic_properties f01_props;
>> +       u8 device_status;
>> +       struct pdt_entry f34_pdt;
>> +       u8 bootloader_id[2];
>> +       struct rmi_f34_queries f34_queries;
>> +       u16 f34_status_address;
>> +       struct rmi_f34_control_status f34_controls;
>> +       const u8 *firmware_data;
>> +       const u8 *config_data;
>> +       struct work_struct reflash_work;
>> +};
> [...]
>> +static int rmi_read_f01_status(struct rmi_reflash_data *data)
>> +{
>> +       int retval;
>> +
>> +       retval = rmi_read(data->rmi_dev, data->f01_pdt.data_base_addr,
>> +                         &data->device_status);
>> +       if (retval)
>> +               return retval;
>> +
>> +       return 0;
>> +}
>
> This function is used one time, just calls rmi_read... There's no need
> to wrap this read.

OK, we'll unwrap this and the others.


>
> [...]
>> +static int rmi_wait_for_idle(struct rmi_reflash_data *data, int timeout_ms)
>> +{
>> +       int timeout_count = ((timeout_ms * 1000) / RMI_MAX_SLEEP_TIME_US) + 1;
>> +       int count = 0;
>> +       struct rmi_f34_control_status *controls = &data->f34_controls;
>> +       int retval;
>> +
>> +       do {
>> +               if (count || timeout_count == 1)
>> +                       usleep_range(RMI_MIN_SLEEP_TIME_US,
>> +                                    RMI_MAX_SLEEP_TIME_US);
>> +               retval = rmi_read_f34_controls(data);
>> +               count++;
>> +               if (retval)
>> +                       continue;
>> +               else if (IS_IDLE(controls)) {
>> +                       if (dev_WARN_ONCE(&data->rmi_dev->dev,
>> +                                         !data->f34_controls.program_enabled,
>> +                                         "Reflash is idle but program_enabled bit isn't set.\n"
>> +                                         ))
>> +                               /*
>> +                                * This works around a bug in certain device
>> +                                * firmwares, where the idle state is reached,
>> +                                * but the program_enabled bit is not yet set.
>> +                                */
>
> If it's a bug in devices, but it's ok to try again as a workaround, is
> there a good reason to print a stacktrace?  Does the user care?

On some devices with this bug, you can generate a lot of log spam by 
warning on each occurrence, possibly wrapping the log to the point where 
any other information is lost on devices with limited buffer.  I felt 
the stack trace was a small overhead to pay.

>> +                               continue;
>> +                       return 0;
>> +               }
>> +       } while (count < timeout_count);
>> +
>> +       dev_err(&data->rmi_dev->dev,
>> +               "ERROR: Timeout waiting for idle status.\n");
>> +       dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
>> +       dev_err(&data->rmi_dev->dev, "Status:  %#04x\n", controls->status);
>> +       dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
>> +                       controls->program_enabled);
>> +       dev_err(&data->rmi_dev->dev, "Idle:    %d\n", IS_IDLE(controls));
>> +       return -ETIMEDOUT;
>> +}
> [...]
>> +static int rmi_write_f34_command(struct rmi_reflash_data *data, u8 command)
>> +{
>> +       int retval;
>> +       struct rmi_device *rmi_dev = data->rmi_dev;
>> +
>> +       retval = rmi_write(rmi_dev, data->f34_status_address, command);
>> +       if (retval < 0) {
>> +               dev_err(&rmi_dev->dev,
>> +                       "Failed to write F34 command %#04x. Code: %d.\n",
>> +                       command, retval);
>> +               return retval;
>> +       }
>> +
>> +       return 0;
>> +}
>
> This function is used three times, and calls one function without any
> wrapping magic.  You could easily call rmi_write in each place.
>
> [...]
>> +static void rmi_reset_device(struct rmi_reflash_data *data)
>
> It really feels like this should have an error return.

I'm not sure that buys us anything.  Regardless of whether we fail or 
succeed, we need to execute the next step after this was called
(rediscover functions), and in the failure case we've already printed an 
appropriate diagnostic message in this routine.

>
>> +{
>> +       int retval;
>> +       const struct rmi_device_platform_data *pdata =
>> +                               rmi_get_platform_data(data->rmi_dev);
>> +
>> +       dev_dbg(&data->rmi_dev->dev, "Resetting...\n");
>> +       retval = rmi_write(data->rmi_dev, data->f01_pdt.command_base_addr,
>> +                          RMI_F01_CMD_DEVICE_RESET);
>> +       if (retval < 0)
>> +               dev_warn(&data->rmi_dev->dev,
>> +                        "WARNING - post-flash reset failed, code: %d.\n",
>> +                        retval);
>> +       msleep(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
>> +       dev_dbg(&data->rmi_dev->dev, "Reset completed.\n");
>> +}
>
>> +static int rmi_write_firmware(struct rmi_reflash_data *data)
>> +{
>> +       return rmi_write_blocks(data, (u8 *) data->firmware_data,
>> +               data->f34_queries.fw_block_count, RMI_F34_WRITE_FW_BLOCK);
>> +}
>
> Called once.
>
>> +
>> +static int rmi_write_configuration(struct rmi_reflash_data *data)
>> +{
>> +       return rmi_write_blocks(data, (u8 *) data->config_data,
>> +               data->f34_queries.config_block_count,
>> +               RMI_F34_WRITE_CONFIG_BLOCK);
>> +}
>
> Called once.
>
>> +
>> +static void rmi_reflash_firmware(struct rmi_reflash_data *data)
>
> Also feels like this should have an error return.

I'm not sure that buys us anything.  Regardless of whether we fail or 
succeed, we need to execute the next two steps after this is called
(reset the device and rediscover functions), and in the failure case 
we've already printed an appropriate diagnostic message closer to the 
actual point of failure.

>
> [...]
>> +static void rmi_fw_update(struct rmi_device *rmi_dev)
>
> Same.

Not sure it's needed here - if we get to the end of this, we're all done 
and there's nothing else we can do if the update didn't work.  We've 
already printed any appropriate diagnostic messages closer to the point 
of failure.

>
>> +{
> [...]
>> +       retval = rmi_read_f34_queries(data);
>> +       if (retval) {
>> +               dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
>> +                       retval);
>> +               goto done;
>> +       }
>> +       if (data->img_name && strlen(data->img_name))
>
> data->img_name && data->img_name[0] ?  No need to waste extra cycles.

That's tidy.  Will apply this here and also as below.

>
>> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
>> +                        rmi_fw_name_format, data->img_name);
>> +       else if (pdata->firmware_name && strlen(pdata->firmware_name))
>
> Same.
>
>> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
>> +                        rmi_fw_name_format, pdata->firmware_name);
>> +       else {
>> +               if (!strlen(data->f01_props.product_id)) {
>
> Same.
>
>> +                       dev_err(&rmi_dev->dev, "Product ID is missing or empty - will not reflash.\n");
>> +                       goto done;
>> +               }
>> +               snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
>> +                        rmi_fw_name_format, data->f01_props.product_id);
>> +       }
> [...]
>> +       if (rmi_go_nogo(data, header)) {
>> +               dev_dbg(&rmi_dev->dev, "Go/NoGo said go.\n");
>> +               rmi_free_function_list(rmi_dev);
>> +               rmi_reflash_firmware(data);
>> +               rmi_reset_device(data);
>> +               rmi_driver_detect_functions(rmi_dev);
>> +       } else
>> +               dev_dbg(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");
>
> Documentation/CodingStyle says:
> } else {

OK

> 	...
> }
>
> [...]
>> +}
> [...]
>> +static ssize_t rmi_reflash_force_store(struct device *dev,
>> +                                      struct device_attribute *attr,
>> +                                      const char *buf, size_t count)
>> +{
>> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
>> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
>> +       struct rmi_reflash_data *data = drv_data->reflash_data;
>> +       int retval;
>> +       unsigned long val;
>> +
>> +       if (test_and_set_bit(0, &data->busy))
>> +               return -EBUSY;
>> +
>> +       retval = kstrtoul(buf, 10, &val);
>> +       if (retval)
>> +               count = retval;
>> +       else
>> +               data->force = !!val;
>
> Hrm. Perhaps '42' doesn't make sense here.  Maybe add:
>
> else if (val > 1)
> 	count = -EINVAL;

OK.


>
>> +
>> +       clear_bit(0, &data->busy);
>> +
>> +       return count;
>> +}
>> +
>> +static ssize_t rmi_reflash_show(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
>> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
>> +       struct rmi_reflash_data *data = drv_data->reflash_data;
>> +
>> +       return snprintf(buf, PAGE_SIZE, "%u\n", test_bit(0, &data->busy));
>> +}
>> +
>> +static ssize_t rmi_reflash_store(struct device *dev,
>> +                                struct device_attribute *attr,
>> +                                const char *buf, size_t count)
>> +{
>> +       int retval;
>> +       unsigned long val;
>> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
>> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
>> +       struct rmi_reflash_data *data = drv_data->reflash_data;
>> +
>> +       retval = kstrtoul(buf, 10, &val);
>> +       if (retval)
>> +               return retval;
>> +
>> +       if (test_and_set_bit(0, &data->busy))
>> +               return -EBUSY;
>> +
>> +       if (val)
>> +               /*
>> +                * TODO: Here we start a work thread to go do the reflash, but
>> +                * maybe we can just use request_firmware_timeout().
>> +                */
>
> Mmm.. Yes.  It would make the lifecycle of this busy bit much more
> obvious.  Otherwise perhaps call request_firmware_nowait() ?  It already
> does this work queue ... uh ... work for you.

Yeah :-)

>
>> +               schedule_work(&data->reflash_work);
>> +       else
>> +               clear_bit(0, &data->busy);
>> +
>> +       return count;
>> +}
> [...]
>> +void rmi_reflash_init(struct rmi_device *rmi_dev)
>> +{
>> +       int error;
>> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
>> +       struct rmi_reflash_data *data;
>> +
>> +       dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
>> +
>> +       data = devm_kzalloc(&rmi_dev->dev, sizeof(struct rmi_reflash_data),
>> +                           GFP_KERNEL);
>
> The memory ownership here is odd.  Maybe kzalloc, and just return that
> data?

That would work.  I'll switch it to kzalloc/kfree.


>> +
>> +       error = sysfs_create_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
>> +       if (error) {
>> +               dev_warn(&rmi_dev->dev, "Failed to create reflash sysfs attributes.\n");
>> +               return;
>> +       }
>> +
>> +       INIT_WORK(&data->reflash_work, rmi_reflash_work);
>> +       data->rmi_dev = rmi_dev;
>> +       drv_data->reflash_data = data;
>> +}
>> +
>> +void rmi_reflash_cleanup(struct rmi_device *rmi_dev)
>> +{
>> +       struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
>> +       struct rmi_reflash_data *data = drv_data->reflash_data;
>> +
>> +       sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
>> +       devm_kfree(&rmi_dev->dev, data);
>
> Who owns this memory again? devm_ doesn't seem right for this use-case.

See above.


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index d0c7b6e..9b88b6a 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -25,6 +25,15 @@  config RMI4_DEBUG
 
 	  If unsure, say N.
 
+config RMI4_FWLIB
+	bool "RMI4 Firmware Update"
+	depends on RMI4_CORE
+	help
+	  Say Y here to enable in-kernel firmware update capability.
+
+	  Add support to the RMI4 core to enable reflashing of device
+	  firmware.
+
 config RMI4_I2C
 	tristate "RMI4 I2C Support"
 	depends on RMI4_CORE && I2C
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 5c6bad5..570ea80 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -1,5 +1,6 @@ 
 obj-$(CONFIG_RMI4_CORE) += rmi_core.o
 rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+rmi_core-$(CONFIG_RMI4_FWLIB) += rmi_fw_update.o
 
 # Function drivers
 obj-$(CONFIG_RMI4_F11) += rmi_f11.o
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 6e0454a..3c93d08 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -117,6 +117,8 @@  int rmi_register_transport_device(struct rmi_transport_dev *xport)
 	if (error)
 		goto err_put_device;
 
+	rmi_reflash_init(rmi_dev);
+
 	dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
 		pdata->sensor_name, dev_name(&rmi_dev->dev));
 
@@ -139,6 +141,7 @@  void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
 	struct rmi_device *rmi_dev = xport->rmi_dev;
 
 	device_del(&rmi_dev->dev);
+	rmi_reflash_cleanup(rmi_dev);
 	rmi_physical_teardown_debugfs(rmi_dev);
 	put_device(&rmi_dev->dev);
 }
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 0cc9089..c49b123 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -29,6 +29,8 @@ 
 
 #define RMI_PDT_PROPS_HAS_BSR 0x02
 
+struct rmi_reflash_data;
+
 struct rmi_driver_data {
 	struct list_head function_list;
 
@@ -81,6 +83,8 @@  struct rmi_driver_data {
 	u8 reg_debug_size;
 #endif
 
+	struct rmi_reflash_data *reflash_data;
+
 	void *data;
 };
 
@@ -114,5 +118,12 @@  int check_bootloader_mode(struct rmi_device *rmi_dev,
 void rmi_free_function_list(struct rmi_device *rmi_dev);
 int rmi_driver_detect_functions(struct rmi_device *rmi_dev);
 
+#ifdef CONFIG_RMI4_FWLIB
+void rmi_reflash_init(struct rmi_device *rmi_dev);
+void rmi_reflash_cleanup(struct rmi_device *rmi_dev);
+#else
+#define rmi_reflash_init(rmi_dev)
+#define rmi_reflash_cleanup(rmi_dev)
+#endif
 
 #endif
diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
new file mode 100644
index 0000000..7118401
--- /dev/null
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -0,0 +1,961 @@ 
+/*
+ * Copyright (c) 2012-2014 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+/* F34 Query register defs. */
+
+#define RMI_F34_QUERY_SIZE		7
+#define RMI_F34_HAS_NEW_REG_MAP		(1 << 0)
+#define RMI_F34_IS_UNLOCKED		(1 << 1)
+#define RMI_F34_HAS_CONFIG_ID		(1 << 2)
+#define RMI_F34_BLOCK_SIZE_OFFSET	1
+#define RMI_F34_FW_BLOCKS_OFFSET	3
+#define RMI_F34_CONFIG_BLOCKS_OFFSET	5
+
+struct rmi_f34_queries {
+	bool	new_reg_map;
+	bool	unlocked;
+	bool	has_config_id;
+	u16	block_size;
+	u16	fw_block_count;
+	u16	config_block_count;
+};
+
+/* F34 Data register defs. */
+
+#define RMI_F34_BLOCK_DATA_OFFSET	2
+
+#define RMI_F34_COMMAND_MASK		0x0F
+#define RMI_F34_STATUS_MASK		0x07
+#define RMI_F34_STATUS_SHIFT		4
+#define RMI_F34_ENABLED_MASK		0x80
+
+#define RMI_F34_WRITE_FW_BLOCK        0x02
+#define RMI_F34_ERASE_ALL             0x03
+#define RMI_F34_WRITE_CONFIG_BLOCK    0x06
+#define RMI_F34_ENABLE_FLASH_PROG     0x0f
+
+struct rmi_f34_control_status {
+	u8 command;
+	u8 status;
+	bool program_enabled;
+};
+
+/* Timeouts for various F34 operations. */
+#define RMI_F34_ENABLE_WAIT_MS 300
+#define RMI_F34_ERASE_WAIT_MS (5 * 1000)
+#define RMI_F34_IDLE_WAIT_MS 500
+
+#define IS_IDLE(ctl_ptr) ((!ctl_ptr->status) && (!ctl_ptr->command))
+
+
+/* Image file defs. */
+#define RMI_IMG_CHECKSUM_OFFSET			0
+#define RMI_IMG_IO_OFFSET			0x06
+#define RMI_IMG_BOOTLOADER_VERSION_OFFSET	0x07
+#define RMI_IMG_IMAGE_SIZE_OFFSET		0x08
+#define RMI_IMG_CONFIG_SIZE_OFFSET		0x0C
+#define RMI_IMG_PACKAGE_ID_OFFSET		0x1A
+#define RMI_IMG_FW_BUILD_ID_OFFSET		0x50
+
+#define RMI_IMG_PRODUCT_INFO_LENGTH		2
+
+#define RMI_IMG_PRODUCT_ID_OFFSET		0x10
+#define RMI_IMG_PRODUCT_INFO_OFFSET		0x1E
+
+#define RMI_F34_FW_IMAGE_OFFSET 0x100
+
+/* Image file V5, Option 0 */
+struct rmi_image_header {
+	u32 checksum;
+	unsigned int image_size;
+	unsigned int config_size;
+	u8 options;
+	u8 io;
+	u32 fw_build_id;
+	u32 package_id;
+	u8 bootloader_version;
+	u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+	u8 product_info[RMI_IMG_PRODUCT_INFO_LENGTH];
+};
+
+static u32 rmi_extract_u32(const u8 *ptr)
+{
+	return (u32)ptr[0] +
+		(u32)ptr[1] * 0x100 +
+		(u32)ptr[2] * 0x10000 +
+		(u32)ptr[3] * 0x1000000;
+}
+
+#define RMI_NAME_BUFFER_SIZE 64
+
+struct rmi_reflash_data {
+	struct rmi_device *rmi_dev;
+	bool force;
+	ulong busy;
+	char name_buf[RMI_NAME_BUFFER_SIZE];
+	char *img_name;
+	struct pdt_entry f01_pdt;
+	struct f01_basic_properties f01_props;
+	u8 device_status;
+	struct pdt_entry f34_pdt;
+	u8 bootloader_id[2];
+	struct rmi_f34_queries f34_queries;
+	u16 f34_status_address;
+	struct rmi_f34_control_status f34_controls;
+	const u8 *firmware_data;
+	const u8 *config_data;
+	struct work_struct reflash_work;
+};
+
+static void rmi_extract_header(const u8 *data, int pos,
+			       struct rmi_image_header *header)
+{
+	header->checksum =
+			rmi_extract_u32(&data[pos + RMI_IMG_CHECKSUM_OFFSET]);
+	header->io = data[pos + RMI_IMG_IO_OFFSET];
+	header->bootloader_version =
+			data[pos + RMI_IMG_BOOTLOADER_VERSION_OFFSET];
+	header->image_size =
+			rmi_extract_u32(&data[pos + RMI_IMG_IMAGE_SIZE_OFFSET]);
+	header->config_size =
+		       rmi_extract_u32(&data[pos + RMI_IMG_CONFIG_SIZE_OFFSET]);
+	if (header->io == 1) {
+		header->fw_build_id =
+		       rmi_extract_u32(&data[pos + RMI_IMG_FW_BUILD_ID_OFFSET]);
+		header->package_id =
+			rmi_extract_u32(&data[pos + RMI_IMG_PACKAGE_ID_OFFSET]);
+	}
+	memcpy(header->product_id, &data[pos + RMI_IMG_PRODUCT_ID_OFFSET],
+	       RMI_PRODUCT_ID_LENGTH);
+	header->product_id[RMI_PRODUCT_ID_LENGTH] = 0;
+	memcpy(header->product_info, &data[pos + RMI_IMG_PRODUCT_INFO_OFFSET],
+	       RMI_IMG_PRODUCT_INFO_LENGTH);
+}
+
+static int rmi_find_functions(struct rmi_device *rmi_dev,
+			      void *ctx, const struct pdt_entry *pdt)
+{
+	struct rmi_reflash_data *data = ctx;
+
+	if (pdt->page_start > 0)
+		return RMI_SCAN_DONE;
+
+	if (pdt->function_number == 0x01)
+		memcpy(&data->f01_pdt, pdt, sizeof(struct pdt_entry));
+	else if (pdt->function_number == 0x34)
+		memcpy(&data->f34_pdt, pdt, sizeof(struct pdt_entry));
+
+	return RMI_SCAN_CONTINUE;
+}
+
+static int rmi_find_f01_and_f34(struct rmi_reflash_data *data)
+{
+	struct rmi_device *rmi_dev = data->rmi_dev;
+	int retval;
+
+	data->f01_pdt.function_number = data->f34_pdt.function_number = 0;
+	retval = rmi_scan_pdt(rmi_dev, data, rmi_find_functions);
+	if (retval < 0)
+		return retval;
+
+	if (!data->f01_pdt.function_number) {
+		dev_err(&rmi_dev->dev, "Failed to find F01 for reflash.\n");
+		return -ENODEV;
+	}
+
+	if (!data->f34_pdt.function_number) {
+		dev_err(&rmi_dev->dev, "Failed to find F34 for reflash.\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int rmi_read_f34_controls(struct rmi_reflash_data *data)
+{
+	int retval;
+	u8 buf;
+
+	retval = rmi_read(data->rmi_dev, data->f34_status_address, &buf);
+	if (retval)
+		return retval;
+
+	data->f34_controls.command = buf & RMI_F34_COMMAND_MASK;
+	data->f34_controls.status = (buf >> RMI_F34_STATUS_SHIFT)
+						& RMI_F34_STATUS_MASK;
+	data->f34_controls.program_enabled = !!(buf & RMI_F34_ENABLED_MASK);
+
+	return 0;
+}
+
+static int rmi_read_f01_status(struct rmi_reflash_data *data)
+{
+	int retval;
+
+	retval = rmi_read(data->rmi_dev, data->f01_pdt.data_base_addr,
+			  &data->device_status);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+#define RMI_MIN_SLEEP_TIME_US 50
+#define RMI_MAX_SLEEP_TIME_US 100
+
+/*
+ * Wait until the status is idle and we're ready to continue.
+ *
+ * In order to indicate that the previous command has succeeded, the
+ * bootloader is supposed to signal idle state by:
+ *
+ *  + atomically do the following by writing 0x80 to F34_FLASH_Data3
+ *     - set status to 0,
+ *     - set command to 0, and
+ *     - leave progam_enabled as 1
+ *  + assert attn
+ *
+ * and then we're supposed to be able to see that we're in IDLE state.
+ * But some (most?) bootloaders do this
+ *
+ *  + clear F34_FLASH_Data3
+ *  + assert attn
+ *  + set the program_enabled bit
+ *
+ * and a significant number of those don't even bother to assert
+ * ATTN, so you've got to poll them (which is what we're doing
+ * here). Regardless of whether you're polling or using ATTN,
+ * there's a race condition between clearing Data3 and setting
+ * program_enabled. So when we lose that race, we emit this warning
+ * (using dev_WARN_ONCE to avoid filling the log with complaints)
+ * and retry a few times. If a correct idle state is reached during
+ * the retries, then we just continue with the process. If it's not
+ * reached (that is, if Data3 contains anything but 0x80 after the
+ * timeout), then something has gone horribly wrong, and we abort the
+ * reflash process.
+ *
+ */
+static int rmi_wait_for_idle(struct rmi_reflash_data *data, int timeout_ms)
+{
+	int timeout_count = ((timeout_ms * 1000) / RMI_MAX_SLEEP_TIME_US) + 1;
+	int count = 0;
+	struct rmi_f34_control_status *controls = &data->f34_controls;
+	int retval;
+
+	do {
+		if (count || timeout_count == 1)
+			usleep_range(RMI_MIN_SLEEP_TIME_US,
+				     RMI_MAX_SLEEP_TIME_US);
+		retval = rmi_read_f34_controls(data);
+		count++;
+		if (retval)
+			continue;
+		else if (IS_IDLE(controls)) {
+			if (dev_WARN_ONCE(&data->rmi_dev->dev,
+					  !data->f34_controls.program_enabled,
+					  "Reflash is idle but program_enabled bit isn't set.\n"
+					  ))
+				/*
+				 * This works around a bug in certain device
+				 * firmwares, where the idle state is reached,
+				 * but the program_enabled bit is not yet set.
+				 */
+				continue;
+			return 0;
+		}
+	} while (count < timeout_count);
+
+	dev_err(&data->rmi_dev->dev,
+		"ERROR: Timeout waiting for idle status.\n");
+	dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
+	dev_err(&data->rmi_dev->dev, "Status:  %#04x\n", controls->status);
+	dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
+			controls->program_enabled);
+	dev_err(&data->rmi_dev->dev, "Idle:    %d\n", IS_IDLE(controls));
+	return -ETIMEDOUT;
+}
+
+static int rmi_read_f34_queries(struct rmi_reflash_data *data)
+{
+	int retval;
+	u8 id_str[3];
+	u8 buf[RMI_F34_QUERY_SIZE];
+
+	retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr,
+				data->bootloader_id, 2);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F34 bootloader_id (code %d).\n",
+			retval);
+		return retval;
+	}
+
+	retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr+2,
+				buf, RMI_F34_QUERY_SIZE);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F34 queries (code %d).\n", retval);
+		return retval;
+	}
+
+	data->f34_queries.new_reg_map = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
+	data->f34_queries.unlocked = buf[0] & RMI_F34_IS_UNLOCKED;
+	data->f34_queries.has_config_id = buf[0] & RMI_F34_HAS_CONFIG_ID;
+	data->f34_queries.block_size =
+		le16_to_cpu(*((u16 *)(buf + RMI_F34_BLOCK_SIZE_OFFSET)));
+	data->f34_queries.fw_block_count =
+		le16_to_cpu(*((u16 *)(buf + RMI_F34_FW_BLOCKS_OFFSET)));
+	data->f34_queries.config_block_count =
+		le16_to_cpu(*((u16 *)(buf + RMI_F34_CONFIG_BLOCKS_OFFSET)));
+
+	id_str[0] = data->bootloader_id[0];
+	id_str[1] = data->bootloader_id[1];
+	id_str[2] = 0;
+
+	dev_dbg(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
+		id_str, data->bootloader_id[0], data->bootloader_id[1]);
+	dev_dbg(&data->rmi_dev->dev, "F34 has config id: %d\n",
+		data->f34_queries.has_config_id);
+	dev_dbg(&data->rmi_dev->dev, "F34 unlocked:      %d\n",
+		data->f34_queries.unlocked);
+	dev_dbg(&data->rmi_dev->dev, "F34 new reg map:   %d\n",
+		data->f34_queries.new_reg_map);
+	dev_dbg(&data->rmi_dev->dev, "F34 block size:    %d\n",
+		data->f34_queries.block_size);
+	dev_dbg(&data->rmi_dev->dev, "F34 fw blocks:     %d\n",
+		data->f34_queries.fw_block_count);
+	dev_dbg(&data->rmi_dev->dev, "F34 config blocks: %d\n",
+		data->f34_queries.config_block_count);
+
+	data->f34_status_address = data->f34_pdt.data_base_addr +
+		RMI_F34_BLOCK_DATA_OFFSET + data->f34_queries.block_size;
+
+	return 0;
+}
+
+static int rmi_write_bootloader_id(struct rmi_reflash_data *data)
+{
+	int retval;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+
+	retval = rmi_write_block(rmi_dev,
+		      data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET,
+		      data->bootloader_id, ARRAY_SIZE(data->bootloader_id));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to write bootloader ID. Code: %d.\n", retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int rmi_write_f34_command(struct rmi_reflash_data *data, u8 command)
+{
+	int retval;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+
+	retval = rmi_write(rmi_dev, data->f34_status_address, command);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to write F34 command %#04x. Code: %d.\n",
+			command, retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int rmi_enter_flash_programming(struct rmi_reflash_data *data)
+{
+	int retval;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+	u8 f01_control_0;
+
+	retval = rmi_write_bootloader_id(data);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&rmi_dev->dev, "Enabling flash programming.\n");
+	retval = rmi_write_f34_command(data, RMI_F34_ENABLE_FLASH_PROG);
+	if (retval < 0)
+		return retval;
+
+	retval = rmi_wait_for_idle(data, RMI_F34_ENABLE_WAIT_MS);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Did not reach idle state after %d ms. Code: %d.\n",
+			RMI_F34_ENABLE_WAIT_MS, retval);
+		return retval;
+	}
+	if (!data->f34_controls.program_enabled) {
+		dev_err(&rmi_dev->dev, "Reached idle, but programming not enabled.\n");
+		return -EINVAL;
+	}
+	dev_dbg(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");
+
+	retval = rmi_find_f01_and_f34(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Failed to rescan pdt.  Code: %d.\n",
+			retval);
+		return retval;
+	}
+
+	retval = rmi_read_f01_status(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Failed to read F01 status after enabling reflash. Code: %d.\n",
+			retval);
+		return retval;
+	}
+	if (!RMI_F01_STATUS_BOOTLOADER(data->device_status)) {
+		dev_err(&rmi_dev->dev, "Device reports as not in flash programming mode.\n");
+		return -EINVAL;
+	}
+
+	retval = rmi_read_f34_queries(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+
+	retval = rmi_read(rmi_dev, data->f01_pdt.control_base_addr,
+			  &f01_control_0);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F01_CTRL_0 read failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+	f01_control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+	f01_control_0 = (f01_control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK)
+			| RMI_SLEEP_MODE_NORMAL;
+
+	retval = rmi_write(rmi_dev, data->f01_pdt.control_base_addr,
+			   f01_control_0);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "F01_CTRL_0 write failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void rmi_reset_device(struct rmi_reflash_data *data)
+{
+	int retval;
+	const struct rmi_device_platform_data *pdata =
+				rmi_get_platform_data(data->rmi_dev);
+
+	dev_dbg(&data->rmi_dev->dev, "Resetting...\n");
+	retval = rmi_write(data->rmi_dev, data->f01_pdt.command_base_addr,
+			   RMI_F01_CMD_DEVICE_RESET);
+	if (retval < 0)
+		dev_warn(&data->rmi_dev->dev,
+			 "WARNING - post-flash reset failed, code: %d.\n",
+			 retval);
+	msleep(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
+	dev_dbg(&data->rmi_dev->dev, "Reset completed.\n");
+}
+
+/*
+ * Send data to the device one block at a time.
+ */
+static int rmi_write_blocks(struct rmi_reflash_data *data, u8 *block_ptr,
+			    u16 block_count, u8 cmd)
+{
+	int block_num;
+	u8 zeros[] = {0, 0};
+	int retval;
+	u16 addr = data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET;
+
+	retval = rmi_write_block(data->rmi_dev, data->f34_pdt.data_base_addr,
+				 zeros, ARRAY_SIZE(zeros));
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev, "Failed to write initial zeros. Code=%d.\n",
+			retval);
+		return retval;
+	}
+
+	for (block_num = 0; block_num < block_count; ++block_num) {
+		retval = rmi_write_block(data->rmi_dev, addr, block_ptr,
+					 data->f34_queries.block_size);
+		if (retval < 0) {
+			dev_err(&data->rmi_dev->dev, "Failed to write block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+		retval = rmi_write_f34_command(data, cmd);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev, "Failed to write command for block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+
+		retval = rmi_wait_for_idle(data, RMI_F34_IDLE_WAIT_MS);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev, "Failed to go idle after writing block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+		block_ptr += data->f34_queries.block_size;
+	}
+
+	return 0;
+}
+
+static int rmi_write_firmware(struct rmi_reflash_data *data)
+{
+	return rmi_write_blocks(data, (u8 *) data->firmware_data,
+		data->f34_queries.fw_block_count, RMI_F34_WRITE_FW_BLOCK);
+}
+
+static int rmi_write_configuration(struct rmi_reflash_data *data)
+{
+	return rmi_write_blocks(data, (u8 *) data->config_data,
+		data->f34_queries.config_block_count,
+		RMI_F34_WRITE_CONFIG_BLOCK);
+}
+
+static void rmi_reflash_firmware(struct rmi_reflash_data *data)
+{
+	struct timespec start;
+	struct timespec end;
+	s64 duration_ns;
+	int retval = 0;
+
+	retval = rmi_enter_flash_programming(data);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev, "Failed to enter flash programming (code: %d).\n",
+			retval);
+		return;
+	}
+
+	retval = rmi_write_bootloader_id(data);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev, "Failed to enter write bootloader ID (code: %d).\n",
+			retval);
+		return;
+	}
+
+	dev_dbg(&data->rmi_dev->dev, "Erasing FW...\n");
+	getnstimeofday(&start);
+	retval = rmi_write_f34_command(data, RMI_F34_ERASE_ALL);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev, "Erase failed (code: %d).\n",
+			retval);
+		return;
+	}
+
+	retval = rmi_wait_for_idle(data, RMI_F34_ERASE_WAIT_MS);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to reach idle state. Code: %d.\n", retval);
+		return;
+	}
+	getnstimeofday(&end);
+	duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+	dev_dbg(&data->rmi_dev->dev,
+		 "Erase complete, time: %lld ns.\n", duration_ns);
+
+	if (data->firmware_data) {
+		dev_dbg(&data->rmi_dev->dev, "Writing firmware...\n");
+		getnstimeofday(&start);
+		retval = rmi_write_firmware(data);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev,
+				"Failed to write FW (code: %d).\n", retval);
+			return;
+		}
+		getnstimeofday(&end);
+		duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+		dev_dbg(&data->rmi_dev->dev,
+			 "Done writing FW, time: %lld ns.\n", duration_ns);
+	}
+
+	if (data->config_data) {
+		dev_dbg(&data->rmi_dev->dev, "Writing configuration...\n");
+		getnstimeofday(&start);
+		retval = rmi_write_configuration(data);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev,
+				"Failed to write config (code: %d).\n", retval);
+			return;
+		}
+		getnstimeofday(&end);
+		duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+		dev_dbg(&data->rmi_dev->dev,
+			 "Done writing config, time: %lld ns.\n", duration_ns);
+	}
+}
+
+static bool rmi_go_nogo(struct rmi_reflash_data *data,
+			struct rmi_image_header *header)
+{
+	if (data->force) {
+		dev_dbg(&data->rmi_dev->dev, "Reflash force flag in effect.\n");
+		return true;
+	}
+
+	if (header->io == 1) {
+		if (header->fw_build_id > data->f01_props.build_id) {
+			dev_dbg(&data->rmi_dev->dev, "Image file has newer Packrat.\n");
+			return true;
+		} else {
+			dev_dbg(&data->rmi_dev->dev, "Image file has lower Packrat ID than device.\n");
+		}
+	}
+
+	return false;
+}
+
+static const char rmi_fw_name_format[] = "%s.img";
+
+static void rmi_fw_update(struct rmi_device *rmi_dev)
+{
+	struct timespec start;
+	struct timespec end;
+	s64 duration_ns;
+	char *firmware_name;
+	const struct firmware *fw_entry = NULL;
+	int retval;
+	struct rmi_image_header *header = NULL;
+	u8 pdt_props;
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+	const struct rmi_device_platform_data *pdata =
+				rmi_get_platform_data(rmi_dev);
+
+	dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+	dev_dbg(&rmi_dev->dev, "force: %d\n", data->force);
+	dev_dbg(&rmi_dev->dev, "img_name: %s\n", data->img_name);
+	dev_dbg(&rmi_dev->dev, "firmware_name: %s\n", pdata->firmware_name);
+
+	getnstimeofday(&start);
+
+	firmware_name = kcalloc(RMI_NAME_BUFFER_SIZE, sizeof(char), GFP_KERNEL);
+	if (!firmware_name) {
+		dev_err(&rmi_dev->dev, "Failed to allocate firmware_name.\n");
+		goto done;
+	}
+	header = kzalloc(sizeof(struct rmi_image_header), GFP_KERNEL);
+	if (!header) {
+		dev_err(&rmi_dev->dev, "Failed to allocate header.\n");
+		goto done;
+	}
+
+	retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &pdt_props);
+	if (retval) {
+		dev_warn(&rmi_dev->dev,
+			 "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
+			 PDT_PROPERTIES_LOCATION, retval);
+	}
+	if (pdt_props & RMI_PDT_PROPS_HAS_BSR) {
+		dev_warn(&rmi_dev->dev,
+			 "Firmware update for LTS not currently supported.\n");
+		goto done;
+	}
+
+	retval = rmi_f01_read_properties(rmi_dev, data->f01_pdt.query_base_addr,
+					 &data->f01_props);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+			retval);
+		goto done;
+	}
+	retval = rmi_read_f34_queries(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+			retval);
+		goto done;
+	}
+	if (data->img_name && strlen(data->img_name))
+		snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+			 rmi_fw_name_format, data->img_name);
+	else if (pdata->firmware_name && strlen(pdata->firmware_name))
+		snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+			 rmi_fw_name_format, pdata->firmware_name);
+	else {
+		if (!strlen(data->f01_props.product_id)) {
+			dev_err(&rmi_dev->dev, "Product ID is missing or empty - will not reflash.\n");
+			goto done;
+		}
+		snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+			 rmi_fw_name_format, data->f01_props.product_id);
+	}
+	dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
+	retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
+	if (retval != 0) {
+		dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
+			firmware_name, retval);
+		goto done;
+	}
+
+	dev_dbg(&rmi_dev->dev, "Got firmware %s, size: %d.\n", firmware_name,
+		fw_entry->size);
+	rmi_extract_header(fw_entry->data, 0, header);
+	dev_dbg(&rmi_dev->dev, "Img checksum:           %#08X\n",
+		header->checksum);
+	dev_dbg(&rmi_dev->dev, "Img io:                 %#04X\n",
+		header->io);
+	dev_dbg(&rmi_dev->dev, "Img image size:         %d\n",
+		header->image_size);
+	dev_dbg(&rmi_dev->dev, "Img config size:        %d\n",
+		header->config_size);
+	dev_dbg(&rmi_dev->dev, "Img bootloader version: %d\n",
+		header->bootloader_version);
+	dev_dbg(&rmi_dev->dev, "Img product id:         %s\n",
+		header->product_id);
+	dev_dbg(&rmi_dev->dev, "Img product info:       %#04x %#04x\n",
+		header->product_info[0], header->product_info[1]);
+	if (header->io == 1) {
+		dev_dbg(&rmi_dev->dev, "Img Packrat:            %d\n",
+			header->fw_build_id);
+		dev_dbg(&rmi_dev->dev, "Img package:            %d\n",
+			header->package_id);
+	}
+
+	if (header->image_size)
+		data->firmware_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET;
+	if (header->config_size)
+		data->config_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET +
+			header->image_size;
+
+	if (rmi_go_nogo(data, header)) {
+		dev_dbg(&rmi_dev->dev, "Go/NoGo said go.\n");
+		rmi_free_function_list(rmi_dev);
+		rmi_reflash_firmware(data);
+		rmi_reset_device(data);
+		rmi_driver_detect_functions(rmi_dev);
+	} else
+		dev_dbg(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");
+
+	if (fw_entry)
+		release_firmware(fw_entry);
+
+
+done:
+	getnstimeofday(&end);
+	duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+	dev_dbg(&rmi_dev->dev, "Time to reflash: %lld ns.\n", duration_ns);
+
+	kfree(firmware_name);
+	kfree(header);
+	return;
+}
+
+/*
+ * We also reflash the device if (a) in kernel reflashing is
+ * enabled, and (b) the reflash module decides it requires reflashing.
+ *
+ * We have to do this before actually building the PDT because the reflash
+ * might cause various registers to move around.
+ */
+static int rmi_device_reflash(struct rmi_device *rmi_dev)
+{
+	struct device *dev = &rmi_dev->dev;
+	struct rmi_driver_data *drv_data = dev_get_drvdata(dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+	int retval;
+
+	retval = rmi_find_f01_and_f34(data);
+	if (retval < 0)
+		return retval;
+
+	rmi_fw_update(rmi_dev);
+
+	clear_bit(0, &data->busy);
+
+	return 0;
+}
+
+static ssize_t rmi_reflash_img_name_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", data->img_name);
+}
+
+static ssize_t rmi_reflash_img_name_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	if (test_and_set_bit(0, &data->busy))
+		return -EBUSY;
+
+	if (!count) {
+		data->img_name = NULL;
+	} else {
+		strlcpy(data->name_buf, buf, count);
+		data->img_name = strstrip(data->name_buf);
+	}
+
+	clear_bit(0, &data->busy);
+	return count;
+}
+
+static ssize_t rmi_reflash_force_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", data->force);
+}
+
+static ssize_t rmi_reflash_force_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+	int retval;
+	unsigned long val;
+
+	if (test_and_set_bit(0, &data->busy))
+		return -EBUSY;
+
+	retval = kstrtoul(buf, 10, &val);
+	if (retval)
+		count = retval;
+	else
+		data->force = !!val;
+
+	clear_bit(0, &data->busy);
+
+	return count;
+}
+
+static ssize_t rmi_reflash_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", test_bit(0, &data->busy));
+}
+
+static ssize_t rmi_reflash_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int retval;
+	unsigned long val;
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	retval = kstrtoul(buf, 10, &val);
+	if (retval)
+		return retval;
+
+	if (test_and_set_bit(0, &data->busy))
+		return -EBUSY;
+
+	if (val)
+		/*
+		 * TODO: Here we start a work thread to go do the reflash, but
+		 * maybe we can just use request_firmware_timeout().
+		 */
+		schedule_work(&data->reflash_work);
+	else
+		clear_bit(0, &data->busy);
+
+	return count;
+}
+
+static void rmi_reflash_work(struct work_struct *work)
+{
+	struct rmi_reflash_data *data =
+		container_of(work, struct rmi_reflash_data, reflash_work);
+	struct rmi_device *rmi_dev = data->rmi_dev;
+	int error;
+
+	dev_dbg(&rmi_dev->dev, "%s runs.\n", __func__);
+	error = rmi_device_reflash(rmi_dev);
+	if (error < 0)
+		dev_err(&rmi_dev->dev, "Reflash attempt failed with code: %d.",
+			error);
+	clear_bit(0, &data->busy);
+}
+
+static DEVICE_ATTR(reflash_force,
+			(S_IRUGO | S_IWUGO),
+			rmi_reflash_force_show, rmi_reflash_force_store);
+static DEVICE_ATTR(reflash_name,
+			(S_IRUGO | S_IWUGO),
+			rmi_reflash_img_name_show, rmi_reflash_img_name_store);
+static DEVICE_ATTR(reflash,
+			(S_IRUGO | S_IWUGO),
+			rmi_reflash_show, rmi_reflash_store);
+
+static struct attribute *rmi_reflash_attrs[] = {
+	&dev_attr_reflash_force.attr,
+	&dev_attr_reflash_name.attr,
+	&dev_attr_reflash.attr,
+	NULL
+};
+
+static const struct attribute_group rmi_reflash_attributes = {
+	.attrs = rmi_reflash_attrs,
+};
+
+void rmi_reflash_init(struct rmi_device *rmi_dev)
+{
+	int error;
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data;
+
+	dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+
+	data = devm_kzalloc(&rmi_dev->dev, sizeof(struct rmi_reflash_data),
+			    GFP_KERNEL);
+
+	error = sysfs_create_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
+	if (error) {
+		dev_warn(&rmi_dev->dev, "Failed to create reflash sysfs attributes.\n");
+		return;
+	}
+
+	INIT_WORK(&data->reflash_work, rmi_reflash_work);
+	data->rmi_dev = rmi_dev;
+	drv_data->reflash_data = data;
+}
+
+void rmi_reflash_cleanup(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_reflash_data *data = drv_data->reflash_data;
+
+	sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
+	devm_kfree(&rmi_dev->dev, data);
+}