@@ -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
@@ -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
@@ -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);
}
@@ -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
new file mode 100644
@@ -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);
+}