Signed-off-by: Christopher Heiny <che...@synaptics.com>

Cc: Dmitry Torokhov <dmitry.torok...@gmail.com>
Cc: Linus Walleij <linus.wall...@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddip...@stericsson.com>
Cc: Joeri de Gram <j.de.g...@gmail.com>

Acked-by: Jean Delvare <kh...@linux-fr.org>

---

 drivers/input/rmi4/rmi_fw_update.c |  724 ++++++++++++++++++++++++++++++++++++
 1 files changed, 724 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_fw_update.c 
b/drivers/input/rmi4/rmi_fw_update.c
new file mode 100644
index 0000000..7f6c315
--- /dev/null
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define DEBUG
+
+#define RIM_HACK 1
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/kernel.h>
+#include<linux/moduleparam.h>
+#include <linux/rmi.h>
+#include <linux/time.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+#include "rmi_f34.h"
+
+#define HAS_BSR_MASK 0x20
+
+#define CHECKSUM_OFFSET 0
+#define BOOTLOADER_VERSION_OFFSET 0x07
+#define IMAGE_SIZE_OFFSET 0x08
+#define CONFIG_SIZE_OFFSET 0x0C
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_ID_SIZE 10
+#define PRODUCT_INFO_OFFSET 0x1E
+#define PRODUCT_INFO_SIZE 2
+
+#define F01_RESET_MASK 0x01
+
+#define ENABLE_WAIT_US (300 * 1000)
+
+/** Image file V5, Option 0
+ */
+struct image_header {
+       u32 checksum;
+       unsigned int image_size;
+       unsigned int config_size;
+       unsigned char options;
+       unsigned char bootloader_version;
+       u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+       unsigned char product_info[PRODUCT_INFO_SIZE];
+};
+
+static u32 extract_u32(const u8 *ptr)
+{
+       return (u32)ptr[0] +
+               (u32)ptr[1] * 0x100 +
+               (u32)ptr[2] * 0x10000 +
+               (u32)ptr[3] * 0x1000000;
+}
+
+struct reflash_data {
+       struct rmi_device *rmi_dev;
+       struct pdt_entry *f01_pdt;
+       union f01_basic_queries f01_queries;
+       union f01_device_control_0 f01_controls;
+       char product_id[RMI_PRODUCT_ID_LENGTH+1];
+       struct pdt_entry *f34_pdt;
+       u8 bootloader_id[2];
+       union f34_query_regs f34_queries;
+       union f34_control_status f34_controls;
+       const u8 *firmware_data;
+       const u8 *config_data;
+};
+
+/* If this parameter is true, we will update the firmware regardless of
+ * the versioning info.
+ */
+static bool force = 1;
+module_param(force, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(param, "Force reflash of RMI4 devices");
+
+/* If this parameter is not NULL, we'll use that name for the firmware image,
+ * instead of getting it from the F01 queries.
+ */
+static char *img_name;
+module_param(img_name, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(param, "Name of the RMI4 firmware image");
+
+#define RMI4_IMAGE_FILE_REV1_OFFSET 30
+#define RMI4_IMAGE_FILE_REV2_OFFSET 31
+#define IMAGE_FILE_CHECKSUM_SIZE 4
+#define FIRMWARE_IMAGE_AREA_OFFSET 0x100
+
+static void extract_header(const u8 *data, int pos, struct image_header 
*header)
+{
+       header->checksum = extract_u32(&data[pos + CHECKSUM_OFFSET]);
+       header->bootloader_version = data[pos + BOOTLOADER_VERSION_OFFSET];
+       header->image_size = extract_u32(&data[pos + IMAGE_SIZE_OFFSET]);
+       header->config_size = extract_u32(&data[pos + CONFIG_SIZE_OFFSET]);
+       memcpy(header->product_id, &data[pos + PRODUCT_ID_OFFSET],
+              RMI_PRODUCT_ID_LENGTH);
+       header->product_id[PRODUCT_ID_SIZE] = 0;
+       memcpy(header->product_info, &data[pos + PRODUCT_INFO_OFFSET],
+              RMI_PRODUCT_ID_LENGTH);
+}
+
+static int rescan_pdt(struct reflash_data *data)
+{
+       int retval;
+       bool f01_found;
+       bool f34_found;
+       struct pdt_entry pdt_entry;
+       int i;
+       struct rmi_device *rmi_dev = data->rmi_dev;
+       struct pdt_entry *f34_pdt = data->f34_pdt;
+       struct pdt_entry *f01_pdt = data->f01_pdt;
+
+       /* Per spec, once we're in reflash we only need to look at the first
+        * PDT page for potentially changed F01 and F34 information.
+        */
+       for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
+                       i -= sizeof(pdt_entry)) {
+               retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+                                       sizeof(pdt_entry));
+               if (retval != sizeof(pdt_entry)) {
+                       dev_err(&rmi_dev->dev,
+                               "Read PDT entry at %#06x failed: %d.\n",
+                               i, retval);
+                       return retval;
+               }
+
+               if (RMI4_END_OF_PDT(pdt_entry.function_number))
+                       break;
+
+               if (pdt_entry.function_number == 0x01) {
+                       memcpy(f01_pdt, &pdt_entry, sizeof(pdt_entry));
+                       f01_found = true;
+               } else if (pdt_entry.function_number == 0x34) {
+                       memcpy(f34_pdt, &pdt_entry, sizeof(pdt_entry));
+                       f34_found = true;
+               }
+       }
+
+       if (!f01_found) {
+               dev_err(&rmi_dev->dev, "Failed to find F01 PDT entry.\n");
+               retval = -ENODEV;
+       } else if (!f34_found) {
+               dev_err(&rmi_dev->dev, "Failed to find F34 PDT entry.\n");
+               retval = -ENODEV;
+       } else
+               retval = 0;
+
+       return retval;
+}
+
+static int read_f34_controls(struct reflash_data *data)
+{
+       int retval;
+
+       retval = rmi_read(data->rmi_dev, data->f34_controls.address,
+                         data->f34_controls.regs);
+       if (retval < 0)
+               return retval;
+
+       return 0;
+}
+
+static int read_f01_status(struct reflash_data *data,
+                          union f01_device_status *device_status)
+{
+       int retval;
+
+       retval = rmi_read(data->rmi_dev, data->f01_pdt->data_base_addr,
+                         device_status->regs);
+       if (retval < 0)
+               return retval;
+
+       return 0;
+}
+
+static int read_f01_controls(struct reflash_data *data)
+{
+       int retval;
+
+       retval = rmi_read(data->rmi_dev, data->f01_pdt->control_base_addr,
+                         data->f01_controls.regs);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+static int write_f01_controls(struct reflash_data *data)
+{
+       int retval;
+
+       retval = rmi_write(data->rmi_dev, data->f01_pdt->control_base_addr,
+                         data->f01_controls.regs[0]);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+#define MIN_SLEEP_TIME_US 50
+#define MAX_SLEEP_TIME_US 100
+
+/* Wait until the status is idle and we're ready to continue */
+static int wait_for_idle(struct reflash_data *data, int timeout_ms)
+{
+       int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+       int count = 0;
+       union f34_control_status *controls = &data->f34_controls;
+       int retval;
+
+       do {
+               if (count || timeout_count == 1)
+                       usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
+               retval = read_f34_controls(data);
+               count++;
+               if (retval < 0)
+                       continue;
+               else if (IS_IDLE(controls)) {
+                       if (!data->f34_controls.program_enabled) {
+                               /** This works around a bug in certain device
+                                * firmwares, where the idle state is reached,
+                                * but the program_enabled bit is not yet set.
+                                */
+                               dev_warn(&data->rmi_dev->dev, "Yikes!  We're 
not enabled!\n");
+                               msleep(1000);
+                               read_f34_controls(data);
+                       }
+                       return 0;
+               }
+       } while (count < timeout_count);
+
+       dev_err(&data->rmi_dev->dev,
+               "ERROR: Timeout waiting for idle status, last status: %#04x.\n",
+               controls->regs[0]);
+       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 read_f01_queries(struct reflash_data *data)
+{
+       int retval;
+       u16 addr = data->f01_pdt->query_base_addr;
+
+       retval = rmi_read_block(data->rmi_dev, addr, data->f01_queries.regs,
+                               ARRAY_SIZE(data->f01_queries.regs));
+       if (retval < 0) {
+               dev_err(&data->rmi_dev->dev,
+                       "Failed to read F34 queries (code %d).\n", retval);
+               return retval;
+       }
+       addr += ARRAY_SIZE(data->f01_queries.regs);
+
+       retval = rmi_read_block(data->rmi_dev, addr, data->product_id,
+                               RMI_PRODUCT_ID_LENGTH);
+       if (retval < 0) {
+               dev_err(&data->rmi_dev->dev,
+                       "Failed to read product ID (code %d).\n", retval);
+               return retval;
+       }
+       data->product_id[RMI_PRODUCT_ID_LENGTH] = 0;
+       dev_info(&data->rmi_dev->dev, "F01 Product id:   %s\n",
+                       data->product_id);
+       dev_info(&data->rmi_dev->dev, "F01 product info: %#04x %#04x\n",
+                       data->f01_queries.productinfo_1,
+                       data->f01_queries.productinfo_2);
+
+       return 0;
+}
+
+static int read_f34_queries(struct reflash_data *data)
+{
+       int retval;
+       u8 id_str[3];
+
+       retval = rmi_read_block(data->rmi_dev, data->f34_pdt->query_base_addr,
+                               data->bootloader_id, 2);
+       if (retval < 0) {
+               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,
+                       data->f34_queries.regs,
+                       ARRAY_SIZE(data->f34_queries.regs));
+       if (retval < 0) {
+               dev_err(&data->rmi_dev->dev,
+                       "Failed to read F34 queries (code %d).\n", retval);
+               return retval;
+       }
+       data->f34_queries.block_size =
+                       le16_to_cpu(data->f34_queries.block_size);
+       data->f34_queries.fw_block_count =
+                       le16_to_cpu(data->f34_queries.fw_block_count);
+       data->f34_queries.config_block_count =
+                       le16_to_cpu(data->f34_queries.config_block_count);
+       id_str[0] = data->bootloader_id[0];
+       id_str[1] = data->bootloader_id[1];
+       id_str[2] = 0;
+#ifdef DEBUG
+       dev_info(&data->rmi_dev->dev, "Got F34 data->f34_queries.\n");
+       dev_info(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
+                id_str, data->bootloader_id[0], data->bootloader_id[1]);
+       dev_info(&data->rmi_dev->dev, "F34 has config id: %d\n",
+                data->f34_queries.has_config_id);
+       dev_info(&data->rmi_dev->dev, "F34 unlocked:      %d\n",
+                data->f34_queries.unlocked);
+       dev_info(&data->rmi_dev->dev, "F34 regMap:        %d\n",
+                data->f34_queries.reg_map);
+       dev_info(&data->rmi_dev->dev, "F34 block size:    %d\n",
+                data->f34_queries.block_size);
+       dev_info(&data->rmi_dev->dev, "F34 fw blocks:     %d\n",
+                data->f34_queries.fw_block_count);
+       dev_info(&data->rmi_dev->dev, "F34 config blocks: %d\n",
+                data->f34_queries.config_block_count);
+#endif
+
+       data->f34_controls.address = data->f34_pdt->data_base_addr +
+                       F34_BLOCK_DATA_OFFSET + data->f34_queries.block_size;
+
+       return 0;
+}
+
+static int write_bootloader_id(struct reflash_data *data)
+{
+       int retval;
+       struct rmi_device *rmi_dev = data->rmi_dev;
+       struct pdt_entry *f34_pdt = data->f34_pdt;
+
+       retval = rmi_write_block(rmi_dev,
+                       f34_pdt->data_base_addr + 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 write_f34_command(struct reflash_data *data, u8 command)
+{
+       int retval;
+       struct rmi_device *rmi_dev = data->rmi_dev;
+
+       retval = rmi_write(rmi_dev, data->f34_controls.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 enter_flash_programming(struct reflash_data *data)
+{
+       int retval;
+       union f01_device_status device_status;
+       struct rmi_device *rmi_dev = data->rmi_dev;
+
+       retval = write_bootloader_id(data);
+       if (retval < 0)
+               return retval;
+
+       dev_info(&rmi_dev->dev, "Enabling flash programming.\n");
+       retval = write_f34_command(data, F34_ENABLE_FLASH_PROG);
+       if (retval < 0)
+               return retval;
+
+#if    RIM_HACK
+       data->f01_controls.nosleep = true;
+       retval = write_f01_controls(data);
+       if (retval < 0)
+               return retval;
+#endif
+
+       retval = wait_for_idle(data, F34_ENABLE_WAIT_MS);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "Did not reach idle state after %d ms. 
Code: %d.\n",
+                       F34_ENABLE_WAIT_MS, retval);
+               return retval;
+       }
+       if (!data->f34_controls.program_enabled) {
+               dev_err(&rmi_dev->dev, "Reached idle, but programming not 
enabled (current status register: %#04x).\n",
+                                       data->f34_controls.regs[0]);
+               return -EINVAL;
+       }
+       dev_info(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");
+
+       retval = rescan_pdt(data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "Failed to rescan pdt.  Code: %d.\n",
+                       retval);
+               return retval;
+       }
+
+       retval = read_f01_status(data, &device_status);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "Failed to read F01 status after 
enabling reflash. Code: %d.\n",
+                       retval);
+               return retval;
+       }
+       if (!(device_status.flash_prog)) {
+               dev_err(&rmi_dev->dev, "Device reports as not in flash 
programming mode.\n");
+               return -EINVAL;
+       }
+
+       retval = read_f34_queries(data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+                       retval);
+               return retval;
+       }
+
+       retval = read_f01_controls(data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "F01 controls read failed, code = %d.\n",
+                       retval);
+               return retval;
+       }
+       data->f01_controls.nosleep = true;
+       data->f01_controls.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+       retval = write_f01_controls(data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "F01 controls write failed, code = 
%d.\n",
+                       retval);
+               return retval;
+       }
+
+       return retval;
+}
+
+static void reset_device(struct reflash_data *data)
+{
+       int retval;
+       struct rmi_device_platform_data *pdata =
+               to_rmi_platform_data(data->rmi_dev);
+
+       dev_info(&data->rmi_dev->dev, "Resetting...\n");
+       retval = rmi_write(data->rmi_dev, data->f01_pdt->command_base_addr,
+                          F01_RESET_MASK);
+       if (retval < 0)
+               dev_warn(&data->rmi_dev->dev,
+                        "WARNING - post-flash reset failed, code: %d.\n",
+                        retval);
+       msleep(pdata->reset_delay_ms);
+       dev_info(&data->rmi_dev->dev, "Reset completed.\n");
+}
+
+/*
+ * Send data to the device one block at a time.
+ */
+static int write_blocks(struct 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 + 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 = 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 = wait_for_idle(data, 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 write_firmware(struct reflash_data *data)
+{
+       return write_blocks(data, (u8 *) data->firmware_data,
+               data->f34_queries.fw_block_count, F34_WRITE_FW_BLOCK);
+}
+
+static int write_configuration(struct reflash_data *data)
+{
+       return write_blocks(data, (u8 *) data->config_data,
+               data->f34_queries.config_block_count, F34_WRITE_CONFIG_BLOCK);
+}
+
+static void reflash_firmware(struct reflash_data *data)
+{
+#ifdef DEBUG
+       struct timespec start;
+       struct timespec end;
+       s64 duration_ns;
+#endif
+       int retval = 0;
+
+       retval = enter_flash_programming(data);
+       if (retval)
+               return;
+
+       retval = write_bootloader_id(data);
+       if (retval)
+               return;
+
+#ifdef DEBUG
+       dev_info(&data->rmi_dev->dev, "Erasing FW...\n");
+       getnstimeofday(&start);
+#endif
+       retval = write_f34_command(data, F34_ERASE_ALL);
+       if (retval)
+               return;
+
+       retval = wait_for_idle(data, F34_ERASE_WAIT_MS);
+       if (retval) {
+               dev_err(&data->rmi_dev->dev,
+                       "Failed to reach idle state. Code: %d.\n", retval);
+               return;
+       }
+#ifdef DEBUG
+       getnstimeofday(&end);
+       duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+       dev_info(&data->rmi_dev->dev,
+                "Erase complete, time: %lld ns.\n", duration_ns);
+#endif
+
+       if (data->firmware_data) {
+#ifdef DEBUG
+               dev_info(&data->rmi_dev->dev, "Writing firmware...\n");
+               getnstimeofday(&start);
+#endif
+               retval = write_firmware(data);
+               if (retval)
+                       return;
+#ifdef DEBUG
+               getnstimeofday(&end);
+               duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+               dev_info(&data->rmi_dev->dev,
+                        "Done writing FW, time: %lld ns.\n", duration_ns);
+#endif
+       }
+
+       if (data->config_data) {
+#ifdef DEBUG
+               dev_info(&data->rmi_dev->dev, "Writing configuration...\n");
+               getnstimeofday(&start);
+#endif
+               retval = write_configuration(data);
+               if (retval)
+                       return;
+#ifdef DEBUG
+               getnstimeofday(&end);
+               duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+               dev_info(&data->rmi_dev->dev,
+                        "Done writing config, time: %lld ns.\n", duration_ns);
+#endif
+       }
+}
+
+/* Returns false if the firmware should not be reflashed.
+ */
+static bool go_nogo(struct reflash_data *data, struct image_header *header)
+{
+       union f01_device_status device_status;
+       int retval;
+
+       if (data->f01_queries.productinfo_1 < header->product_info[0] ||
+               data->f01_queries.productinfo_2 < header->product_info[1]) {
+               dev_info(&data->rmi_dev->dev,
+                        "FW product ID is older than image product ID.\n");
+               return true;
+       }
+
+       retval = read_f01_status(data, &device_status);
+       if (retval)
+               dev_err(&data->rmi_dev->dev,
+                       "Failed to read F01 status. Code: %d.\n", retval);
+
+       return device_status.flash_prog || force;
+}
+
+void rmi4_fw_update(struct rmi_device *rmi_dev,
+               struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt)
+{
+#ifdef DEBUG
+       struct timespec start;
+       struct timespec end;
+       s64 duration_ns;
+#endif
+       char firmware_name[RMI_PRODUCT_ID_LENGTH + 12];
+       const struct firmware *fw_entry = NULL;
+       int retval;
+       struct image_header header;
+       union pdt_properties pdt_props;
+       struct reflash_data data = {
+               .rmi_dev = rmi_dev,
+               .f01_pdt = f01_pdt,
+               .f34_pdt = f34_pdt,
+       };
+       struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+       dev_info(&rmi_dev->dev, "%s called.\n", __func__);
+#ifdef DEBUG
+       getnstimeofday(&start);
+#endif
+
+       retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, pdt_props.regs);
+       if (retval < 0) {
+               dev_warn(&rmi_dev->dev,
+                        "Failed to read PDT props at %#06x (code %d). Assuming 
0x00.\n",
+                        PDT_PROPERTIES_LOCATION, retval);
+       }
+       if (pdt_props.has_bsr) {
+               dev_warn(&rmi_dev->dev,
+                        "Firmware update for LTS not currently supported.\n");
+               return;
+       }
+
+       retval = read_f01_queries(&data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+                       retval);
+               return;
+       }
+       retval = read_f34_queries(&data);
+       if (retval) {
+               dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+                       retval);
+               return;
+       }
+       if (pdata->firmware_name && strlen(pdata->firmware_name))
+               snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
+                       pdata->firmware_name);
+       else
+               snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
+                       (img_name && strlen(img_name))
+                               ? img_name : data.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);
+               return;
+       }
+
+#ifdef DEBUG
+       dev_info(&rmi_dev->dev, "Got firmware, size: %d.\n", fw_entry->size);
+       extract_header(fw_entry->data, 0, &header);
+       dev_info(&rmi_dev->dev, "Img checksum:           %#08X\n",
+                header.checksum);
+       dev_info(&rmi_dev->dev, "Img image size:         %d\n",
+                header.image_size);
+       dev_info(&rmi_dev->dev, "Img config size:        %d\n",
+                header.config_size);
+       dev_info(&rmi_dev->dev, "Img bootloader version: %d\n",
+                header.bootloader_version);
+       dev_info(&rmi_dev->dev, "Img product id:         %s\n",
+                header.product_id);
+       dev_info(&rmi_dev->dev, "Img product info:       %#04x %#04x\n",
+                header.product_info[0], header.product_info[1]);
+#endif
+
+       if (header.image_size)
+               data.firmware_data = fw_entry->data + F34_FW_IMAGE_OFFSET;
+       if (header.config_size)
+               data.config_data = fw_entry->data + F34_FW_IMAGE_OFFSET +
+                       header.image_size;
+
+       if (go_nogo(&data, &header)) {
+               reflash_firmware(&data);
+               reset_device(&data);
+       } else
+               dev_info(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");
+
+       if (fw_entry)
+               release_firmware(fw_entry);
+#ifdef DEBUG
+       getnstimeofday(&end);
+       duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+       dev_info(&rmi_dev->dev, "Time to reflash: %lld ns.\n", duration_ns);
+#endif
+}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to