blob: aff460c13f25749386cf0225fc7b265e7825a130 [file] [log] [blame]
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#ifdef HTC_FEATURE
#include <linux/wakelock.h>
#endif
#define FW_IMAGE_NAME "synaptics.img"
#define FW_IMAGE_NAME_BL71 "synaptics_bl71.img"
#define FW_IMAGE_NAME_BL77 "synaptics_bl77.img"
#define DO_STARTUP_FW_UPDATE
#define SYNA_SIMPLE_UPDATE
/*
#ifdef DO_STARTUP_FW_UPDATE
#ifdef CONFIG_FB
#define WAIT_FOR_FB_READY
#define FB_READY_WAIT_MS 100
#define FB_READY_TIMEOUT_S 30
#endif
#endif
*/
#define MAX_WRITE_SIZE 4096
#define FORCE_UPDATE false
#define DO_LOCKDOWN false
#define MAX_IMAGE_NAME_LEN 256
#define MAX_FIRMWARE_ID_LEN 10
#define IMAGE_HEADER_VERSION_05 0x05
#define IMAGE_HEADER_VERSION_06 0x06
#define IMAGE_HEADER_VERSION_10 0x10
#define IMAGE_AREA_OFFSET 0x100
#define LOCKDOWN_SIZE 0x50
#define MAX_UTILITY_PARAMS 20
#define V5V6_BOOTLOADER_ID_OFFSET 0
#define V5V6_CONFIG_ID_SIZE 4
#define V5_PROPERTIES_OFFSET 2
#define V5_BLOCK_SIZE_OFFSET 3
#define V5_BLOCK_COUNT_OFFSET 5
#define V5_BLOCK_NUMBER_OFFSET 0
#define V5_BLOCK_DATA_OFFSET 2
#define V6_PROPERTIES_OFFSET 1
#define V6_BLOCK_SIZE_OFFSET 2
#define V6_BLOCK_COUNT_OFFSET 3
#define V6_PROPERTIES_2_OFFSET 4
#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5
#define V6_BLOCK_NUMBER_OFFSET 0
#define V6_BLOCK_DATA_OFFSET 1
#define V6_FLASH_COMMAND_OFFSET 2
#define V6_FLASH_STATUS_OFFSET 3
#define V7_CONFIG_ID_SIZE 32
#define V7_FLASH_STATUS_OFFSET 0
#define V7_PARTITION_ID_OFFSET 1
#define V7_BLOCK_NUMBER_OFFSET 2
#define V7_TRANSFER_LENGTH_OFFSET 3
#define V7_COMMAND_OFFSET 4
#define V7_PAYLOAD_OFFSET 5
#define V7_PARTITION_SUPPORT_BYTES 4
#define F35_ERROR_CODE_OFFSET 0
#define F35_FLASH_STATUS_OFFSET 5
#define F35_CHUNK_NUM_LSB_OFFSET 0
#define F35_CHUNK_NUM_MSB_OFFSET 1
#define F35_CHUNK_DATA_OFFSET 2
#define F35_CHUNK_COMMAND_OFFSET 18
#define F35_CHUNK_SIZE 16
#define F35_ERASE_ALL_WAIT_MS 5000
#define F35_RESET_WAIT_MS 250
#define SLEEP_MODE_NORMAL (0x00)
#define SLEEP_MODE_SENSOR_SLEEP (0x01)
#define SLEEP_MODE_RESERVED0 (0x02)
#define SLEEP_MODE_RESERVED1 (0x03)
#define ENABLE_WAIT_MS (1 * 1000)
#define WRITE_WAIT_MS (3 * 1000)
#define ERASE_WAIT_MS (5 * 1000)
#define MIN_SLEEP_TIME_US 50
#define MAX_SLEEP_TIME_US 100
#define INT_DISABLE_WAIT_MS 20
#define ENTER_FLASH_PROG_WAIT_MS 20
static int fwu_do_reflash(void);
static int fwu_recovery_check_status(void);
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
static ssize_t fwu_sysfs_store_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
static ssize_t fwu_sysfs_do_recovery_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_write_config_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_read_config_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_config_area_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_image_name_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_image_size_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fwu_sysfs_block_size_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
#endif
enum f34_version {
F34_V0 = 0,
F34_V1,
F34_V2,
};
enum bl_version {
BL_V5 = 5,
BL_V6 = 6,
BL_V7 = 7,
BL_V8 = 8,
};
enum flash_area {
NONE = 0,
UI_FIRMWARE,
UI_CONFIG,
};
enum update_mode {
NORMAL = 1,
FORCE = 2,
LOCKDOWN = 8,
};
enum config_area {
UI_CONFIG_AREA = 0,
PM_CONFIG_AREA,
BL_CONFIG_AREA,
DP_CONFIG_AREA,
FLASH_CONFIG_AREA,
};
enum v7_status {
SUCCESS = 0x00,
DEVICE_NOT_IN_BOOTLOADER_MODE,
INVALID_PARTITION,
INVALID_COMMAND,
INVALID_BLOCK_OFFSET,
INVALID_TRANSFER,
NOT_ERASED,
FLASH_PROGRAMMING_KEY_INCORRECT,
BAD_PARTITION_TABLE,
CHECKSUM_FAILED,
FLASH_HARDWARE_FAILURE = 0x1f,
};
enum v7_partition_id {
BOOTLOADER_PARTITION = 0x01,
DEVICE_CONFIG_PARTITION,
FLASH_CONFIG_PARTITION,
MANUFACTURING_BLOCK_PARTITION,
GUEST_SERIALIZATION_PARTITION,
GLOBAL_PARAMETERS_PARTITION,
CORE_CODE_PARTITION,
CORE_CONFIG_PARTITION,
GUEST_CODE_PARTITION,
DISPLAY_CONFIG_PARTITION,
EXTERNAL_TOUCH_AFE_CONFIG_PARTITION,
UTILITY_PARAMETER_PARTITION,
};
enum v7_flash_command {
CMD_V7_IDLE = 0x00,
CMD_V7_ENTER_BL,
CMD_V7_READ,
CMD_V7_WRITE,
CMD_V7_ERASE,
CMD_V7_ERASE_AP,
CMD_V7_SENSOR_ID,
};
enum v5v6_flash_command {
CMD_V5V6_IDLE = 0x0,
CMD_V5V6_WRITE_FW = 0x2,
CMD_V5V6_ERASE_ALL = 0x3,
CMD_V5V6_WRITE_LOCKDOWN = 0x4,
CMD_V5V6_READ_CONFIG = 0x5,
CMD_V5V6_WRITE_CONFIG = 0x6,
CMD_V5V6_ERASE_UI_CONFIG = 0x7,
#ifdef HTC_FEATURE
CMD_V5V6_SENSOR_ID = 0x8,
#endif
CMD_V5V6_ERASE_BL_CONFIG = 0x9,
CMD_V5V6_ERASE_DISP_CONFIG = 0xa,
CMD_V5V6_ERASE_GUEST_CODE = 0xb,
CMD_V5V6_WRITE_GUEST_CODE = 0xc,
CMD_V5V6_ENABLE_FLASH_PROG = 0xf,
};
enum flash_command {
CMD_IDLE = 0,
CMD_WRITE_FW,
CMD_WRITE_CONFIG,
CMD_WRITE_LOCKDOWN,
CMD_WRITE_GUEST_CODE,
CMD_WRITE_BOOTLOADER,
CMD_WRITE_UTILITY_PARAM,
CMD_READ_CONFIG,
CMD_ERASE_ALL,
CMD_ERASE_UI_FIRMWARE,
CMD_ERASE_UI_CONFIG,
CMD_ERASE_BL_CONFIG,
CMD_ERASE_DISP_CONFIG,
CMD_ERASE_FLASH_CONFIG,
CMD_ERASE_GUEST_CODE,
CMD_ERASE_BOOTLOADER,
CMD_ERASE_UTILITY_PARAMETER,
CMD_ENABLE_FLASH_PROG,
#ifdef HTC_FEATURE
CMD_READ_SENSOR_ID,
#endif
};
enum f35_flash_command {
CMD_F35_IDLE = 0x0,
CMD_F35_RESERVED = 0x1,
CMD_F35_WRITE_CHUNK = 0x2,
CMD_F35_ERASE_ALL = 0x3,
CMD_F35_RESET = 0x10,
};
enum container_id {
TOP_LEVEL_CONTAINER = 0,
UI_CONTAINER,
UI_CONFIG_CONTAINER,
BL_CONTAINER,
BL_IMAGE_CONTAINER,
BL_CONFIG_CONTAINER,
BL_LOCKDOWN_INFO_CONTAINER,
PERMANENT_CONFIG_CONTAINER,
GUEST_CODE_CONTAINER,
BL_PROTOCOL_DESCRIPTOR_CONTAINER,
UI_PROTOCOL_DESCRIPTOR_CONTAINER,
RMI_SELF_DISCOVERY_CONTAINER,
RMI_PAGE_CONTENT_CONTAINER,
GENERAL_INFORMATION_CONTAINER,
DEVICE_CONFIG_CONTAINER,
FLASH_CONFIG_CONTAINER,
GUEST_SERIALIZATION_CONTAINER,
GLOBAL_PARAMETERS_CONTAINER,
CORE_CODE_CONTAINER,
CORE_CONFIG_CONTAINER,
DISPLAY_CONFIG_CONTAINER,
EXTERNAL_TOUCH_AFE_CONFIG_CONTAINER,
UTILITY_CONTAINER,
UTILITY_PARAMETER_CONTAINER,
};
enum utility_parameter_id {
UNUSED = 0,
FORCE_PARAMETER,
ANTI_BENDING_PARAMETER,
};
struct pdt_properties {
union {
struct {
unsigned char reserved_1:6;
unsigned char has_bsr:1;
unsigned char reserved_2:1;
} __packed;
unsigned char data[1];
};
};
struct partition_table {
unsigned char partition_id:5;
unsigned char byte_0_reserved:3;
unsigned char byte_1_reserved;
unsigned char partition_length_7_0;
unsigned char partition_length_15_8;
unsigned char start_physical_address_7_0;
unsigned char start_physical_address_15_8;
unsigned char partition_properties_7_0;
unsigned char partition_properties_15_8;
} __packed;
struct f01_device_control {
union {
struct {
unsigned char sleep_mode:2;
unsigned char nosleep:1;
unsigned char reserved:2;
unsigned char charger_connected:1;
unsigned char report_rate:1;
unsigned char configured:1;
} __packed;
unsigned char data[1];
};
};
struct f34_v7_query_0 {
union {
struct {
unsigned char subpacket_1_size:3;
unsigned char has_config_id:1;
unsigned char f34_query0_b4:1;
unsigned char has_thqa:1;
unsigned char f34_query0_b6__7:2;
} __packed;
unsigned char data[1];
};
};
struct f34_v7_query_1_7 {
union {
struct {
/* query 1 */
unsigned char bl_minor_revision;
unsigned char bl_major_revision;
/* query 2 */
unsigned char bl_fw_id_7_0;
unsigned char bl_fw_id_15_8;
unsigned char bl_fw_id_23_16;
unsigned char bl_fw_id_31_24;
/* query 3 */
unsigned char minimum_write_size;
unsigned char block_size_7_0;
unsigned char block_size_15_8;
unsigned char flash_page_size_7_0;
unsigned char flash_page_size_15_8;
/* query 4 */
unsigned char adjustable_partition_area_size_7_0;
unsigned char adjustable_partition_area_size_15_8;
/* query 5 */
unsigned char flash_config_length_7_0;
unsigned char flash_config_length_15_8;
/* query 6 */
unsigned char payload_length_7_0;
unsigned char payload_length_15_8;
/* query 7 */
unsigned char f34_query7_b0:1;
unsigned char has_bootloader:1;
unsigned char has_device_config:1;
unsigned char has_flash_config:1;
unsigned char has_manufacturing_block:1;
unsigned char has_guest_serialization:1;
unsigned char has_global_parameters:1;
unsigned char has_core_code:1;
unsigned char has_core_config:1;
unsigned char has_guest_code:1;
unsigned char has_display_config:1;
unsigned char f34_query7_b11__15:5;
unsigned char f34_query7_b16__23;
unsigned char f34_query7_b24__31;
} __packed;
unsigned char data[21];
};
};
struct f34_v7_data0 {
union {
struct {
unsigned char operation_status:5;
unsigned char device_cfg_status:2;
unsigned char bl_mode:1;
} __packed;
unsigned char data[1];
};
};
struct f34_v7_data_1_5 {
union {
struct {
unsigned char partition_id:5;
unsigned char f34_data1_b5__7:3;
unsigned char block_offset_7_0;
unsigned char block_offset_15_8;
unsigned char transfer_length_7_0;
unsigned char transfer_length_15_8;
unsigned char command;
unsigned char payload_0;
unsigned char payload_1;
} __packed;
unsigned char data[8];
};
};
struct f34_v5v6_flash_properties {
union {
struct {
unsigned char reg_map:1;
unsigned char unlocked:1;
unsigned char has_config_id:1;
unsigned char has_pm_config:1;
unsigned char has_bl_config:1;
unsigned char has_disp_config:1;
unsigned char has_ctrl1:1;
unsigned char has_query4:1;
} __packed;
unsigned char data[1];
};
};
struct f34_v5v6_flash_properties_2 {
union {
struct {
unsigned char has_guest_code:1;
unsigned char reserved:7;
} __packed;
unsigned char data[1];
};
};
struct register_offset {
unsigned char properties;
unsigned char properties_2;
unsigned char block_size;
unsigned char block_count;
unsigned char gc_block_count;
unsigned char flash_status;
unsigned char partition_id;
unsigned char block_number;
unsigned char transfer_length;
unsigned char flash_cmd;
unsigned char payload;
};
struct block_count {
unsigned short ui_firmware;
unsigned short ui_config;
unsigned short dp_config;
unsigned short pm_config;
unsigned short fl_config;
unsigned short bl_image;
unsigned short bl_config;
unsigned short utility_param;
unsigned short lockdown;
unsigned short guest_code;
unsigned short total_count;
};
struct physical_address {
unsigned short ui_firmware;
unsigned short ui_config;
unsigned short dp_config;
unsigned short pm_config;
unsigned short fl_config;
unsigned short bl_image;
unsigned short bl_config;
unsigned short utility_param;
unsigned short lockdown;
unsigned short guest_code;
};
struct container_descriptor {
unsigned char content_checksum[4];
unsigned char container_id[2];
unsigned char minor_version;
unsigned char major_version;
unsigned char reserved_08;
unsigned char reserved_09;
unsigned char reserved_0a;
unsigned char reserved_0b;
unsigned char container_option_flags[4];
unsigned char content_options_length[4];
unsigned char content_options_address[4];
unsigned char content_length[4];
unsigned char content_address[4];
};
struct image_header_10 {
unsigned char checksum[4];
unsigned char reserved_04;
unsigned char reserved_05;
unsigned char minor_header_version;
unsigned char major_header_version;
unsigned char reserved_08;
unsigned char reserved_09;
unsigned char reserved_0a;
unsigned char reserved_0b;
unsigned char top_level_container_start_addr[4];
};
struct image_header_05_06 {
/* 0x00 - 0x0f */
unsigned char checksum[4];
unsigned char reserved_04;
unsigned char reserved_05;
unsigned char options_firmware_id:1;
unsigned char options_bootloader:1;
unsigned char options_guest_code:1;
unsigned char options_tddi:1;
unsigned char options_reserved:4;
unsigned char header_version;
unsigned char firmware_size[4];
unsigned char config_size[4];
/* 0x10 - 0x1f */
unsigned char product_id[PRODUCT_ID_SIZE];
unsigned char package_id[2];
unsigned char package_id_revision[2];
unsigned char product_info[PRODUCT_INFO_SIZE];
/* 0x20 - 0x2f */
unsigned char bootloader_addr[4];
unsigned char bootloader_size[4];
unsigned char ui_addr[4];
unsigned char ui_size[4];
/* 0x30 - 0x3f */
unsigned char ds_id[16];
/* 0x40 - 0x4f */
union {
struct {
unsigned char cstmr_product_id[PRODUCT_ID_SIZE];
unsigned char reserved_4a_4f[6];
};
struct {
unsigned char dsp_cfg_addr[4];
unsigned char dsp_cfg_size[4];
unsigned char reserved_48_4f[8];
};
};
/* 0x50 - 0x53 */
unsigned char firmware_id[4];
};
struct block_data {
unsigned int size;
const unsigned char *data;
};
struct image_metadata {
bool contains_firmware_id;
bool contains_bootloader;
bool contains_guest_code;
bool contains_disp_config;
bool contains_perm_config;
bool contains_flash_config;
bool contains_utility_param;
unsigned int firmware_id;
unsigned int checksum;
unsigned int bootloader_size;
unsigned int disp_config_offset;
unsigned char bl_version;
unsigned char product_id[PRODUCT_ID_SIZE + 1];
unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1];
unsigned char utility_param_id[MAX_UTILITY_PARAMS];
struct block_data bootloader;
struct block_data utility;
struct block_data ui_firmware;
struct block_data ui_config;
struct block_data dp_config;
struct block_data pm_config;
struct block_data fl_config;
struct block_data bl_image;
struct block_data bl_config;
struct block_data utility_param[MAX_UTILITY_PARAMS];
struct block_data lockdown;
struct block_data guest_code;
struct block_count blkcount;
struct physical_address phyaddr;
};
struct synaptics_rmi4_fwu_handle {
enum bl_version bl_version;
bool initialized;
bool in_bl_mode;
bool in_ub_mode;
bool bl_mode_device;
bool force_update;
bool do_lockdown;
bool has_guest_code;
bool has_utility_param;
bool new_partition_table;
bool incompatible_partition_tables;
bool write_bootloader;
unsigned int data_pos;
unsigned char *ext_data_source;
unsigned char *read_config_buf;
unsigned char intr_mask;
unsigned char command;
unsigned char bootloader_id[2];
unsigned char config_id[32];
unsigned char flash_status;
unsigned char partitions;
#ifdef F51_DISCRETE_FORCE
unsigned char *cal_data;
unsigned short cal_data_off;
unsigned short cal_data_size;
unsigned short cal_data_buf_size;
unsigned short cal_packet_data_size;
#endif
unsigned short block_size;
unsigned short config_size;
unsigned short config_area;
unsigned short config_block_count;
unsigned short flash_config_length;
unsigned short payload_length;
unsigned short partition_table_bytes;
unsigned short read_config_buf_size;
const unsigned char *config_data;
const unsigned char *image;
unsigned char *image_name;
unsigned int image_size;
struct image_metadata img;
struct register_offset off;
struct block_count blkcount;
struct physical_address phyaddr;
struct f34_v5v6_flash_properties flash_properties;
struct synaptics_rmi4_fn_desc f34_fd;
struct synaptics_rmi4_fn_desc f35_fd;
struct synaptics_rmi4_data *rmi4_data;
struct workqueue_struct *fwu_workqueue;
struct work_struct fwu_work;
#ifdef HTC_FEATURE
struct wake_lock fwu_wake_lock;
#endif
};
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
.mode = (S_IRUGO | S_IWUGO),
},
.size = 0,
.read = fwu_sysfs_show_image,
.write = fwu_sysfs_store_image,
};
static struct device_attribute attrs[] = {
__ATTR(dorecovery, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_do_recovery_store),
__ATTR(doreflash, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_do_reflash_store),
__ATTR(writeconfig, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_write_config_store),
__ATTR(readconfig, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_read_config_store),
__ATTR(configarea, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_config_area_store),
__ATTR(imagename, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_image_name_store),
__ATTR(imagesize, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_image_size_store),
__ATTR(blocksize, S_IRUGO,
fwu_sysfs_block_size_show,
synaptics_rmi4_store_error),
__ATTR(fwblockcount, S_IRUGO,
fwu_sysfs_firmware_block_count_show,
synaptics_rmi4_store_error),
__ATTR(configblockcount, S_IRUGO,
fwu_sysfs_configuration_block_count_show,
synaptics_rmi4_store_error),
__ATTR(dispconfigblockcount, S_IRUGO,
fwu_sysfs_disp_config_block_count_show,
synaptics_rmi4_store_error),
__ATTR(permconfigblockcount, S_IRUGO,
fwu_sysfs_perm_config_block_count_show,
synaptics_rmi4_store_error),
__ATTR(blconfigblockcount, S_IRUGO,
fwu_sysfs_bl_config_block_count_show,
synaptics_rmi4_store_error),
__ATTR(guestcodeblockcount, S_IRUGO,
fwu_sysfs_guest_code_block_count_show,
synaptics_rmi4_store_error),
__ATTR(writeguestcode, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
fwu_sysfs_write_guest_code_store),
};
#endif
static struct synaptics_rmi4_fwu_handle *fwu;
DECLARE_COMPLETION(fwu_remove_complete);
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
DEFINE_MUTEX(fwu_sysfs_mutex);
#endif
/* Check offset + size <= bound. true if in bounds, false otherwise. */
static bool in_bounds(unsigned long offset, unsigned long size,
unsigned long bound)
{
if (offset > bound || size > bound) {
pr_err("%s: %lu or %lu > %lu\n", __func__, offset, size, bound);
return false;
}
if (offset > (bound - size)) {
pr_err("%s: %lu > %lu - %lu\n", __func__, offset, size, bound);
return false;
}
return true;
}
#ifdef HTC_FEATURE
static uint32_t syn_crc(uint16_t *data, uint32_t len)
{
uint32_t sum1, sum2;
sum1 = sum2 = 0xFFFF;
if (data) {
while (len--) {
sum1 += *data++;
sum2 += sum1;
sum1 = (sum1 & 0xFFFF) + (sum1 >> 16);
sum2 = (sum2 & 0xFFFF) + (sum2 >> 16);
}
} else {
pr_err("%s: data incorrect", __func__);
return (0xFFFF | 0xFFFF << 16);
}
return sum1 | (sum2 << 16);
}
#endif
#ifndef SYNA_SIMPLE_UPDATE
static void calculate_checksum(unsigned short *data, unsigned long len,
unsigned long *result)
{
unsigned long temp;
unsigned long sum1 = 0xffff;
unsigned long sum2 = 0xffff;
*result = 0xffffffff;
while (len--) {
temp = *data;
sum1 += temp;
sum2 += sum1;
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
data++;
}
*result = sum2 << 16 | sum1;
return;
}
static void convert_to_little_endian(unsigned char *dest, unsigned long src)
{
dest[0] = (unsigned char)(src & 0xff);
dest[1] = (unsigned char)((src >> 8) & 0xff);
dest[2] = (unsigned char)((src >> 16) & 0xff);
dest[3] = (unsigned char)((src >> 24) & 0xff);
return;
}
#endif
static unsigned int le_to_uint(const unsigned char *ptr)
{
return (unsigned int)ptr[0] +
(unsigned int)ptr[1] * 0x100 +
(unsigned int)ptr[2] * 0x10000 +
(unsigned int)ptr[3] * 0x1000000;
}
#ifdef F51_DISCRETE_FORCE
static int fwu_f51_force_data_init(void)
{
int retval;
unsigned char query_count;
unsigned char packet_info;
unsigned char offset[2];
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f51_query_base_addr + 7,
offset,
sizeof(offset));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read force data offset\n",
__func__);
return retval;
}
fwu->cal_data_off = offset[0] | offset[1] << 8;
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f51_query_base_addr,
&query_count,
sizeof(query_count));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read number of F51 query registers\n",
__func__);
return retval;
}
if (query_count >= 10) {
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f51_query_base_addr + 9,
&packet_info,
sizeof(packet_info));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F51 packet register info\n",
__func__);
return retval;
}
if (packet_info & MASK_1BIT) {
fwu->cal_packet_data_size = packet_info >> 1;
fwu->cal_packet_data_size *= 2;
} else {
fwu->cal_packet_data_size = 0;
}
} else {
fwu->cal_packet_data_size = 0;
}
fwu->cal_data_size = CAL_DATA_SIZE + fwu->cal_packet_data_size;
if (fwu->cal_data_size > fwu->cal_data_buf_size) {
kfree(fwu->cal_data);
fwu->cal_data_buf_size = fwu->cal_data_size;
fwu->cal_data = kmalloc(fwu->cal_data_buf_size, GFP_KERNEL);
if (!fwu->cal_data) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for fwu->cal_data\n",
__func__);
fwu->cal_data_buf_size = 0;
return -ENOMEM;
}
}
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_allocate_read_config_buf(unsigned int count)
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (count > fwu->read_config_buf_size) {
kfree(fwu->read_config_buf);
fwu->read_config_buf = kzalloc(count, GFP_KERNEL);
if (!fwu->read_config_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for fwu->read_config_buf\n",
__func__);
fwu->read_config_buf_size = 0;
return -ENOMEM;
}
fwu->read_config_buf_size = count;
}
return 0;
}
#endif
static void fwu_compare_partition_tables(void)
{
fwu->incompatible_partition_tables = false;
if (fwu->phyaddr.bl_image != fwu->img.phyaddr.bl_image)
fwu->incompatible_partition_tables = true;
else if (fwu->phyaddr.lockdown != fwu->img.phyaddr.lockdown)
fwu->incompatible_partition_tables = true;
else if (fwu->phyaddr.bl_config != fwu->img.phyaddr.bl_config)
fwu->incompatible_partition_tables = true;
else if (fwu->phyaddr.utility_param != fwu->img.phyaddr.utility_param)
fwu->incompatible_partition_tables = true;
if (fwu->bl_version == BL_V7) {
if (fwu->phyaddr.fl_config != fwu->img.phyaddr.fl_config)
fwu->incompatible_partition_tables = true;
}
fwu->new_partition_table = false;
if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware)
fwu->new_partition_table = true;
else if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config)
fwu->new_partition_table = true;
if (fwu->flash_properties.has_disp_config) {
if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config)
fwu->new_partition_table = true;
}
if (fwu->has_guest_code) {
if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code)
fwu->new_partition_table = true;
}
return;
}
static int fwu_parse_partition_table(const unsigned char *partition_table,
unsigned long len,
struct block_count *blkcount,
struct physical_address *phyaddr)
{
unsigned char ii;
unsigned char index;
unsigned char offset;
unsigned short partition_length;
unsigned short physical_address;
struct partition_table *ptable;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
for (ii = 0; ii < fwu->partitions; ii++) {
index = ii * 8 + 2;
if (!in_bounds(index, sizeof(*ptable), len)) {
pr_err("%s: %d/%d not in bounds\n", __func__, ii,
fwu->partitions);
return -EINVAL;
}
ptable = (struct partition_table *)&partition_table[index];
partition_length = ptable->partition_length_15_8 << 8 |
ptable->partition_length_7_0;
physical_address = ptable->start_physical_address_15_8 << 8 |
ptable->start_physical_address_7_0;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Partition entry %d:\n",
__func__, ii);
for (offset = 0; offset < sizeof(*ptable); offset++) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: 0x%02x\n",
__func__,
partition_table[index + offset]);
}
switch (ptable->partition_id) {
case CORE_CODE_PARTITION:
blkcount->ui_firmware = partition_length;
phyaddr->ui_firmware = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Core code block count: %d\n",
__func__, blkcount->ui_firmware);
blkcount->total_count += partition_length;
break;
case CORE_CONFIG_PARTITION:
blkcount->ui_config = partition_length;
phyaddr->ui_config = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Core config block count: %d\n",
__func__, blkcount->ui_config);
blkcount->total_count += partition_length;
break;
case BOOTLOADER_PARTITION:
blkcount->bl_image = partition_length;
phyaddr->bl_image = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Bootloader block count: %d\n",
__func__, blkcount->bl_image);
blkcount->total_count += partition_length;
break;
case UTILITY_PARAMETER_PARTITION:
blkcount->utility_param = partition_length;
phyaddr->utility_param = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Utility parameter block count: %d\n",
__func__, blkcount->utility_param);
blkcount->total_count += partition_length;
break;
case DISPLAY_CONFIG_PARTITION:
blkcount->dp_config = partition_length;
phyaddr->dp_config = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Display config block count: %d\n",
__func__, blkcount->dp_config);
blkcount->total_count += partition_length;
break;
case FLASH_CONFIG_PARTITION:
blkcount->fl_config = partition_length;
phyaddr->fl_config = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Flash config block count: %d\n",
__func__, blkcount->fl_config);
blkcount->total_count += partition_length;
break;
case GUEST_CODE_PARTITION:
blkcount->guest_code = partition_length;
phyaddr->guest_code = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Guest code block count: %d\n",
__func__, blkcount->guest_code);
blkcount->total_count += partition_length;
break;
case GUEST_SERIALIZATION_PARTITION:
blkcount->pm_config = partition_length;
phyaddr->pm_config = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Guest serialization block count: %d\n",
__func__, blkcount->pm_config);
blkcount->total_count += partition_length;
break;
case GLOBAL_PARAMETERS_PARTITION:
blkcount->bl_config = partition_length;
phyaddr->bl_config = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Global parameters block count: %d\n",
__func__, blkcount->bl_config);
blkcount->total_count += partition_length;
break;
case DEVICE_CONFIG_PARTITION:
blkcount->lockdown = partition_length;
phyaddr->lockdown = physical_address;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Device config block count: %d\n",
__func__, blkcount->lockdown);
blkcount->total_count += partition_length;
break;
};
}
return 0;
}
static int fwu_parse_image_header_10_utility(const unsigned char *image)
{
unsigned char ii;
unsigned char num_of_containers;
unsigned int addr;
unsigned int container_id;
unsigned int length;
unsigned int content_offset;
const unsigned char *content;
struct container_descriptor *descriptor;
num_of_containers = fwu->img.utility.size / 4;
if (num_of_containers)
fwu->img.contains_utility_param = true;
for (ii = 0; ii < num_of_containers; ii++) {
if (ii >= MAX_UTILITY_PARAMS)
continue;
addr = le_to_uint(fwu->img.utility.data + (ii * 4));
if (!in_bounds(addr, sizeof(*descriptor), fwu->image_size))
return -EINVAL;
descriptor = (struct container_descriptor *)(image + addr);
container_id = descriptor->container_id[0] |
descriptor->container_id[1] << 8;
content_offset = le_to_uint(descriptor->content_address);
length = le_to_uint(descriptor->content_length);
if (!in_bounds(content_offset, length, fwu->image_size))
return -EINVAL;
content = image + content_offset;
switch (container_id) {
case UTILITY_PARAMETER_CONTAINER:
fwu->img.utility_param[ii].data = content;
fwu->img.utility_param[ii].size = length;
if (length < sizeof(content[0]))
return -EINVAL;
fwu->img.utility_param_id[ii] = content[0];
break;
default:
break;
};
}
return 0;
}
static int fwu_parse_image_header_10_bootloader(const unsigned char *image)
{
unsigned char ii;
unsigned char num_of_containers;
unsigned int addr;
unsigned int container_id;
unsigned int length;
unsigned int content_offset;
const unsigned char *content;
struct container_descriptor *descriptor;
if (fwu->img.bootloader.size < 4)
return -EINVAL;
num_of_containers = (fwu->img.bootloader.size - 4) / 4;
for (ii = 1; ii <= num_of_containers; ii++) {
addr = le_to_uint(fwu->img.bootloader.data + (ii * 4));
if (!in_bounds(addr, sizeof(*descriptor), fwu->image_size))
return -EINVAL;
descriptor = (struct container_descriptor *)(image + addr);
container_id = descriptor->container_id[0] |
descriptor->container_id[1] << 8;
content_offset = le_to_uint(descriptor->content_address);
length = le_to_uint(descriptor->content_length);
if (!in_bounds(content_offset, length, fwu->image_size))
return -EINVAL;
content = image + content_offset;
switch (container_id) {
case BL_IMAGE_CONTAINER:
fwu->img.bl_image.data = content;
fwu->img.bl_image.size = length;
break;
case BL_CONFIG_CONTAINER:
case GLOBAL_PARAMETERS_CONTAINER:
fwu->img.bl_config.data = content;
fwu->img.bl_config.size = length;
break;
case BL_LOCKDOWN_INFO_CONTAINER:
case DEVICE_CONFIG_CONTAINER:
fwu->img.lockdown.data = content;
fwu->img.lockdown.size = length;
break;
default:
break;
};
}
return 0;
}
static int fwu_parse_image_header_10(void)
{
unsigned char ii;
unsigned char num_of_containers;
unsigned int addr;
unsigned int offset;
unsigned int content_offset;
unsigned int container_id;
unsigned int length;
unsigned int image_size;
const unsigned char *image;
const unsigned char *content;
struct container_descriptor *descriptor;
struct image_header_10 *header;
image = fwu->image;
image_size = fwu->image_size;
if (image_size < sizeof(*header))
return -EINVAL;
header = (struct image_header_10 *)image;
fwu->img.checksum = le_to_uint(header->checksum);
/* address of top level container */
offset = le_to_uint(header->top_level_container_start_addr);
if (!in_bounds(offset, sizeof(*descriptor), image_size))
return -EINVAL;
descriptor = (struct container_descriptor *)(image + offset);
/* address of top level container content */
offset = le_to_uint(descriptor->content_address);
num_of_containers = le_to_uint(descriptor->content_length) / 4;
for (ii = 0; ii < num_of_containers; ii++) {
if (!in_bounds(offset, 4, image_size))
return -EINVAL;
addr = le_to_uint(image + offset);
offset += 4;
if (!in_bounds(addr, sizeof(*descriptor), image_size))
return -EINVAL;
descriptor = (struct container_descriptor *)(image + addr);
container_id = descriptor->container_id[0] |
descriptor->container_id[1] << 8;
content_offset = le_to_uint(descriptor->content_address);
length = le_to_uint(descriptor->content_length);
if (!in_bounds(content_offset, length, image_size))
return -EINVAL;
content = image + content_offset;
switch (container_id) {
case UI_CONTAINER:
case CORE_CODE_CONTAINER:
fwu->img.ui_firmware.data = content;
fwu->img.ui_firmware.size = length;
break;
case UI_CONFIG_CONTAINER:
case CORE_CONFIG_CONTAINER:
fwu->img.ui_config.data = content;
fwu->img.ui_config.size = length;
break;
case BL_CONTAINER:
fwu->img.bl_version = *content;
fwu->img.bootloader.data = content;
fwu->img.bootloader.size = length;
if (fwu_parse_image_header_10_bootloader(image))
return -EINVAL;
break;
case UTILITY_CONTAINER:
fwu->img.utility.data = content;
fwu->img.utility.size = length;
if (fwu_parse_image_header_10_utility(image))
return -EINVAL;
break;
case GUEST_CODE_CONTAINER:
fwu->img.contains_guest_code = true;
fwu->img.guest_code.data = content;
fwu->img.guest_code.size = length;
break;
case DISPLAY_CONFIG_CONTAINER:
fwu->img.contains_disp_config = true;
fwu->img.dp_config.data = content;
fwu->img.dp_config.size = length;
break;
case PERMANENT_CONFIG_CONTAINER:
case GUEST_SERIALIZATION_CONTAINER:
fwu->img.contains_perm_config = true;
fwu->img.pm_config.data = content;
fwu->img.pm_config.size = length;
break;
case FLASH_CONFIG_CONTAINER:
fwu->img.contains_flash_config = true;
fwu->img.fl_config.data = content;
fwu->img.fl_config.size = length;
break;
case GENERAL_INFORMATION_CONTAINER:
fwu->img.contains_firmware_id = true;
if (length < 4 + 4)
return -EINVAL;
fwu->img.firmware_id = le_to_uint(content + 4);
break;
default:
break;
}
}
return 0;
}
static int fwu_parse_image_header_05_06(void)
{
int retval;
const unsigned char *image;
struct image_header_05_06 *header;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
image = fwu->image;
if (fwu->image_size < sizeof(*header))
return -EINVAL;
header = (struct image_header_05_06 *)image;
fwu->img.checksum = le_to_uint(header->checksum);
fwu->img.bl_version = header->header_version;
fwu->img.contains_bootloader = header->options_bootloader;
if (fwu->img.contains_bootloader)
fwu->img.bootloader_size = le_to_uint(header->bootloader_size);
fwu->img.ui_firmware.size = le_to_uint(header->firmware_size);
if (fwu->img.ui_firmware.size) {
unsigned int ui_firmware_offset = IMAGE_AREA_OFFSET;
if (fwu->img.contains_bootloader) {
if (!in_bounds(ui_firmware_offset,
fwu->img.bootloader_size,
fwu->image_size)) {
return -EINVAL;
}
ui_firmware_offset += fwu->img.bootloader_size;
}
if (!in_bounds(ui_firmware_offset,
fwu->img.ui_firmware.size,
fwu->image_size)) {
return -EINVAL;
}
fwu->img.ui_firmware.data = image + ui_firmware_offset;
}
if ((fwu->img.bl_version == BL_V6) && header->options_tddi) {
if (!in_bounds(IMAGE_AREA_OFFSET,
fwu->img.ui_firmware.size,
fwu->image_size)) {
return -EINVAL;
}
fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
}
fwu->img.ui_config.size = le_to_uint(header->config_size);
if (fwu->img.ui_config.size) {
unsigned int ui_firmware_end;
if (fwu->img.ui_firmware.data < image)
return -EINVAL;
if (!in_bounds(fwu->img.ui_firmware.data - image,
fwu->img.ui_firmware.size,
fwu->image_size)) {
return -EINVAL;
}
ui_firmware_end = fwu->img.ui_firmware.data - image +
fwu->img.ui_firmware.size;
if (!in_bounds(ui_firmware_end, fwu->img.ui_config.size,
fwu->image_size)) {
return -EINVAL;
}
fwu->img.ui_config.data = image + ui_firmware_end;
}
if ((fwu->img.bl_version == BL_V5 && fwu->img.contains_bootloader) ||
(fwu->img.bl_version == BL_V6 && header->options_tddi))
fwu->img.contains_disp_config = true;
else
fwu->img.contains_disp_config = false;
if (fwu->img.contains_disp_config) {
fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr);
fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size);
if (!in_bounds(fwu->img.disp_config_offset,
fwu->img.dp_config.size,
fwu->image_size)) {
return -EINVAL;
}
fwu->img.dp_config.data = image + fwu->img.disp_config_offset;
} else {
retval = secure_memcpy(fwu->img.cstmr_product_id,
sizeof(fwu->img.cstmr_product_id),
header->cstmr_product_id,
sizeof(header->cstmr_product_id),
PRODUCT_ID_SIZE);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy custom product ID string\n",
__func__);
}
fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0;
}
fwu->img.contains_firmware_id = header->options_firmware_id;
if (fwu->img.contains_firmware_id)
fwu->img.firmware_id = le_to_uint(header->firmware_id);
retval = secure_memcpy(fwu->img.product_id,
sizeof(fwu->img.product_id),
header->product_id,
sizeof(header->product_id),
PRODUCT_ID_SIZE);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy product ID string\n",
__func__);
}
fwu->img.product_id[PRODUCT_ID_SIZE] = 0;
if (LOCKDOWN_SIZE > IMAGE_AREA_OFFSET)
return -EINVAL;
if (fwu->image_size < IMAGE_AREA_OFFSET)
return -EINVAL;
fwu->img.lockdown.size = LOCKDOWN_SIZE;
fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE;
return 0;
}
static int fwu_parse_image_info(void)
{
int parse_retval;
struct image_header_10 *header;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
unsigned int image_size = 0;
header = (struct image_header_10 *)fwu->image;
if (!header)
return -EINVAL;
image_size = fwu->image_size;
if (image_size < sizeof(struct image_header_05_06) &&
image_size < sizeof(struct image_header_10)) {
return -EINVAL;
}
/* This is clearing img, not image. */
memset(&fwu->img, 0x00, sizeof(fwu->img));
switch (header->major_header_version) {
case IMAGE_HEADER_VERSION_10:
parse_retval = fwu_parse_image_header_10();
break;
case IMAGE_HEADER_VERSION_05:
case IMAGE_HEADER_VERSION_06:
parse_retval = fwu_parse_image_header_05_06();
break;
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Unsupported image file format (0x%02x)\n",
__func__, header->major_header_version);
return -EINVAL;
}
if (parse_retval != 0) {
return -EINVAL;
}
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) {
if (!fwu->img.contains_flash_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: No flash config found in firmware image\n",
__func__);
return -EINVAL;
}
if (fwu_parse_partition_table(fwu->img.fl_config.data,
fwu->img.fl_config.size,
&fwu->img.blkcount,
&fwu->img.phyaddr)) {
return -EINVAL;
}
fwu_compare_partition_tables();
} else {
fwu->new_partition_table = false;
fwu->incompatible_partition_tables = false;
}
return 0;
}
static int fwu_read_flash_status(void)
{
int retval;
unsigned char status;
unsigned char command;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + fwu->off.flash_status,
&status,
sizeof(status));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash status\n",
__func__);
return retval;
}
fwu->in_bl_mode = status >> 7;
if (fwu->bl_version == BL_V5)
fwu->flash_status = (status >> 4) & MASK_3BIT;
else if (fwu->bl_version == BL_V6)
fwu->flash_status = status & MASK_3BIT;
else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
fwu->flash_status = status & MASK_5BIT;
if (fwu->write_bootloader)
fwu->flash_status = 0x00;
if (fwu->flash_status != 0x00) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Flash status = %d, command = 0x%02x\n",
__func__, fwu->flash_status, fwu->command);
}
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + fwu->off.flash_cmd,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash command\n",
__func__);
return retval;
}
if (fwu->bl_version == BL_V5)
fwu->command = command & MASK_4BIT;
else if (fwu->bl_version == BL_V6)
fwu->command = command & MASK_6BIT;
else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
fwu->command = command;
if (fwu->write_bootloader)
fwu->command = 0x00;
return 0;
}
static int fwu_wait_for_idle(int timeout_ms, bool poll)
{
int count = 0;
int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
do {
usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
count++;
if (poll || (count == timeout_count))
fwu_read_flash_status();
if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00))
return 0;
} while (count < timeout_count);
dev_err(rmi4_data->pdev->dev.parent,
"%s: Timed out waiting for idle status\n",
__func__);
return -ETIMEDOUT;
}
static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd)
{
int retval;
unsigned char data_base;
struct f34_v7_data_1_5 data_1_5;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
memset(data_1_5.data, 0x00, sizeof(data_1_5.data));
switch (cmd) {
case CMD_ERASE_ALL:
data_1_5.partition_id = CORE_CODE_PARTITION;
data_1_5.command = CMD_V7_ERASE_AP;
break;
case CMD_ERASE_UI_FIRMWARE:
data_1_5.partition_id = CORE_CODE_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_BL_CONFIG:
data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_UI_CONFIG:
data_1_5.partition_id = CORE_CONFIG_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_DISP_CONFIG:
data_1_5.partition_id = DISPLAY_CONFIG_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_FLASH_CONFIG:
data_1_5.partition_id = FLASH_CONFIG_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_GUEST_CODE:
data_1_5.partition_id = GUEST_CODE_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_BOOTLOADER:
data_1_5.partition_id = BOOTLOADER_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ERASE_UTILITY_PARAMETER:
data_1_5.partition_id = UTILITY_PARAMETER_PARTITION;
data_1_5.command = CMD_V7_ERASE;
break;
case CMD_ENABLE_FLASH_PROG:
data_1_5.partition_id = BOOTLOADER_PARTITION;
data_1_5.command = CMD_V7_ENTER_BL;
break;
};
data_1_5.payload_0 = fwu->bootloader_id[0];
data_1_5.payload_1 = fwu->bootloader_id[1];
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.partition_id,
data_1_5.data,
sizeof(data_1_5.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write single transaction command\n",
__func__);
return retval;
}
return 0;
}
static int fwu_write_f34_v7_command(unsigned char cmd)
{
int retval;
unsigned char data_base;
unsigned char command;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
switch (cmd) {
case CMD_WRITE_FW:
case CMD_WRITE_CONFIG:
case CMD_WRITE_LOCKDOWN:
case CMD_WRITE_GUEST_CODE:
case CMD_WRITE_BOOTLOADER:
case CMD_WRITE_UTILITY_PARAM:
command = CMD_V7_WRITE;
break;
case CMD_READ_CONFIG:
command = CMD_V7_READ;
break;
case CMD_ERASE_ALL:
command = CMD_V7_ERASE_AP;
break;
case CMD_ERASE_UI_FIRMWARE:
case CMD_ERASE_BL_CONFIG:
case CMD_ERASE_UI_CONFIG:
case CMD_ERASE_DISP_CONFIG:
case CMD_ERASE_FLASH_CONFIG:
case CMD_ERASE_GUEST_CODE:
case CMD_ERASE_BOOTLOADER:
case CMD_ERASE_UTILITY_PARAMETER:
command = CMD_V7_ERASE;
break;
case CMD_ENABLE_FLASH_PROG:
command = CMD_V7_ENTER_BL;
break;
#ifdef HTC_FEATURE
case CMD_READ_SENSOR_ID:
command = CMD_V7_SENSOR_ID;
break;
#endif
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid command 0x%02x\n",
__func__, cmd);
return -EINVAL;
};
fwu->command = command;
switch (cmd) {
case CMD_ERASE_ALL:
case CMD_ERASE_UI_FIRMWARE:
case CMD_ERASE_BL_CONFIG:
case CMD_ERASE_UI_CONFIG:
case CMD_ERASE_DISP_CONFIG:
case CMD_ERASE_FLASH_CONFIG:
case CMD_ERASE_GUEST_CODE:
case CMD_ERASE_BOOTLOADER:
case CMD_ERASE_UTILITY_PARAMETER:
case CMD_ENABLE_FLASH_PROG:
retval = fwu_write_f34_v7_command_single_transaction(cmd);
if (retval < 0)
return retval;
else
return 0;
default:
break;
};
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.flash_cmd,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write flash command\n",
__func__);
return retval;
}
return 0;
}
static int fwu_write_f34_v5v6_command(unsigned char cmd)
{
int retval;
unsigned char data_base;
unsigned char command;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
switch (cmd) {
case CMD_IDLE:
command = CMD_V5V6_IDLE;
break;
case CMD_WRITE_FW:
command = CMD_V5V6_WRITE_FW;
break;
case CMD_WRITE_CONFIG:
command = CMD_V5V6_WRITE_CONFIG;
break;
case CMD_WRITE_LOCKDOWN:
command = CMD_V5V6_WRITE_LOCKDOWN;
break;
case CMD_WRITE_GUEST_CODE:
command = CMD_V5V6_WRITE_GUEST_CODE;
break;
case CMD_READ_CONFIG:
command = CMD_V5V6_READ_CONFIG;
break;
case CMD_ERASE_ALL:
command = CMD_V5V6_ERASE_ALL;
break;
case CMD_ERASE_UI_CONFIG:
command = CMD_V5V6_ERASE_UI_CONFIG;
break;
case CMD_ERASE_DISP_CONFIG:
command = CMD_V5V6_ERASE_DISP_CONFIG;
break;
case CMD_ERASE_GUEST_CODE:
command = CMD_V5V6_ERASE_GUEST_CODE;
break;
case CMD_ENABLE_FLASH_PROG:
command = CMD_V5V6_ENABLE_FLASH_PROG;
break;
#ifdef HTC_FEATURE
case CMD_READ_SENSOR_ID:
command = CMD_V5V6_SENSOR_ID;
break;
#endif
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid command 0x%02x\n",
__func__, cmd);
return -EINVAL;
}
switch (cmd) {
case CMD_ERASE_ALL:
case CMD_ERASE_UI_CONFIG:
case CMD_ERASE_DISP_CONFIG:
case CMD_ERASE_GUEST_CODE:
case CMD_ENABLE_FLASH_PROG:
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.payload,
fwu->bootloader_id,
sizeof(fwu->bootloader_id));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write bootloader ID\n",
__func__);
return retval;
}
break;
default:
break;
};
fwu->command = command;
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.flash_cmd,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write command 0x%02x\n",
__func__, command);
return retval;
}
return 0;
}
static int fwu_write_f34_command(unsigned char cmd)
{
int retval;
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
retval = fwu_write_f34_v7_command(cmd);
else
retval = fwu_write_f34_v5v6_command(cmd);
return retval;
}
static int fwu_write_f34_v7_partition_id(unsigned char cmd)
{
int retval;
unsigned char data_base;
unsigned char partition;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
switch (cmd) {
case CMD_WRITE_FW:
partition = CORE_CODE_PARTITION;
break;
case CMD_WRITE_CONFIG:
case CMD_READ_CONFIG:
if (fwu->config_area == UI_CONFIG_AREA)
partition = CORE_CONFIG_PARTITION;
else if (fwu->config_area == DP_CONFIG_AREA)
partition = DISPLAY_CONFIG_PARTITION;
else if (fwu->config_area == PM_CONFIG_AREA)
partition = GUEST_SERIALIZATION_PARTITION;
else if (fwu->config_area == BL_CONFIG_AREA)
partition = GLOBAL_PARAMETERS_PARTITION;
else if (fwu->config_area == FLASH_CONFIG_AREA)
partition = FLASH_CONFIG_PARTITION;
break;
case CMD_WRITE_LOCKDOWN:
partition = DEVICE_CONFIG_PARTITION;
break;
case CMD_WRITE_GUEST_CODE:
partition = GUEST_CODE_PARTITION;
break;
case CMD_WRITE_BOOTLOADER:
partition = BOOTLOADER_PARTITION;
break;
case CMD_WRITE_UTILITY_PARAM:
partition = UTILITY_PARAMETER_PARTITION;
break;
case CMD_ERASE_ALL:
partition = CORE_CODE_PARTITION;
break;
case CMD_ERASE_BL_CONFIG:
partition = GLOBAL_PARAMETERS_PARTITION;
break;
case CMD_ERASE_UI_CONFIG:
partition = CORE_CONFIG_PARTITION;
break;
case CMD_ERASE_DISP_CONFIG:
partition = DISPLAY_CONFIG_PARTITION;
break;
case CMD_ERASE_FLASH_CONFIG:
partition = FLASH_CONFIG_PARTITION;
break;
case CMD_ERASE_GUEST_CODE:
partition = GUEST_CODE_PARTITION;
break;
case CMD_ERASE_BOOTLOADER:
partition = BOOTLOADER_PARTITION;
break;
case CMD_ENABLE_FLASH_PROG:
partition = BOOTLOADER_PARTITION;
break;
#ifdef HTC_FEATURE
case CMD_READ_SENSOR_ID:
partition = BOOTLOADER_PARTITION;
break;
#endif
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid command 0x%02x\n",
__func__, cmd);
return -EINVAL;
};
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.partition_id,
&partition,
sizeof(partition));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write partition ID\n",
__func__);
return retval;
}
return 0;
}
static int fwu_write_f34_partition_id(unsigned char cmd)
{
int retval;
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
retval = fwu_write_f34_v7_partition_id(cmd);
else
retval = 0;
return retval;
}
static int fwu_read_f34_v7_partition_table(unsigned char *partition_table)
{
int retval;
unsigned char data_base;
unsigned char length[2];
unsigned short block_number = 0;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
fwu->config_area = FLASH_CONFIG_AREA;
retval = fwu_write_f34_partition_id(CMD_READ_CONFIG);
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.block_number,
(unsigned char *)&block_number,
sizeof(block_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block number\n",
__func__);
return retval;
}
length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT);
length[1] = (unsigned char)(fwu->flash_config_length >> 8);
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.transfer_length,
length,
sizeof(length));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write transfer length\n",
__func__);
return retval;
}
retval = fwu_write_f34_command(CMD_READ_CONFIG);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write command\n",
__func__);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, true);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status\n",
__func__);
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
data_base + fwu->off.payload,
partition_table,
fwu->partition_table_bytes);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block data\n",
__func__);
return retval;
}
return 0;
}
static int fwu_read_f34_v7_queries(void)
{
int retval;
unsigned char ii;
unsigned char query_base;
unsigned char index;
unsigned char offset;
unsigned char *ptable;
struct f34_v7_query_0 query_0;
struct f34_v7_query_1_7 query_1_7;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
query_base = fwu->f34_fd.query_base_addr;
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base,
query_0.data,
sizeof(query_0.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read query 0\n",
__func__);
return retval;
}
offset = query_0.subpacket_1_size + 1;
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + offset,
query_1_7.data,
sizeof(query_1_7.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read queries 1 to 7\n",
__func__);
return retval;
}
fwu->bootloader_id[0] = query_1_7.bl_minor_revision;
fwu->bootloader_id[1] = query_1_7.bl_major_revision;
if (fwu->bootloader_id[1] == BL_V8)
fwu->bl_version = BL_V8;
fwu->block_size = query_1_7.block_size_15_8 << 8 |
query_1_7.block_size_7_0;
fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 |
query_1_7.flash_config_length_7_0;
fwu->payload_length = query_1_7.payload_length_15_8 << 8 |
query_1_7.payload_length_7_0;
fwu->off.flash_status = V7_FLASH_STATUS_OFFSET;
fwu->off.partition_id = V7_PARTITION_ID_OFFSET;
fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET;
fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
fwu->off.flash_cmd = V7_COMMAND_OFFSET;
fwu->off.payload = V7_PAYLOAD_OFFSET;
index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES;
fwu->partitions = 0;
for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) {
for (ii = 0; ii < 8; ii++) {
if (query_1_7.data[index + offset] & (1 << ii))
fwu->partitions++;
}
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Supported partitions: 0x%02x\n",
__func__, query_1_7.data[index + offset]);
}
fwu->partition_table_bytes = fwu->partitions * 8 + 2;
ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL);
if (!ptable) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for partition table\n",
__func__);
return -ENOMEM;
}
retval = fwu_read_f34_v7_partition_table(ptable);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read partition table\n",
__func__);
kfree(ptable);
return retval;
}
if (fwu_parse_partition_table(ptable, fwu->partition_table_bytes,
&fwu->blkcount, &fwu->phyaddr)) {
kfree(ptable);
return -EINVAL;
}
if (fwu->blkcount.dp_config)
fwu->flash_properties.has_disp_config = 1;
else
fwu->flash_properties.has_disp_config = 0;
if (fwu->blkcount.pm_config)
fwu->flash_properties.has_pm_config = 1;
else
fwu->flash_properties.has_pm_config = 0;
if (fwu->blkcount.bl_config)
fwu->flash_properties.has_bl_config = 1;
else
fwu->flash_properties.has_bl_config = 0;
if (fwu->blkcount.guest_code)
fwu->has_guest_code = 1;
else
fwu->has_guest_code = 0;
if (fwu->blkcount.utility_param)
fwu->has_utility_param = 1;
else
fwu->has_utility_param = 0;
kfree(ptable);
return 0;
}
static int fwu_read_f34_v5v6_queries(void)
{
int retval;
unsigned char count;
unsigned char query_base;
unsigned char buf[10];
struct f34_v5v6_flash_properties_2 properties_2;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
query_base = fwu->f34_fd.query_base_addr;
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + V5V6_BOOTLOADER_ID_OFFSET,
fwu->bootloader_id,
sizeof(fwu->bootloader_id));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read bootloader ID\n",
__func__);
return retval;
}
if (fwu->bl_version == BL_V5) {
fwu->off.properties = V5_PROPERTIES_OFFSET;
fwu->off.block_size = V5_BLOCK_SIZE_OFFSET;
fwu->off.block_count = V5_BLOCK_COUNT_OFFSET;
fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET;
fwu->off.payload = V5_BLOCK_DATA_OFFSET;
} else if (fwu->bl_version == BL_V6) {
fwu->off.properties = V6_PROPERTIES_OFFSET;
fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET;
fwu->off.block_size = V6_BLOCK_SIZE_OFFSET;
fwu->off.block_count = V6_BLOCK_COUNT_OFFSET;
fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET;
fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET;
fwu->off.payload = V6_BLOCK_DATA_OFFSET;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + fwu->off.block_size,
buf,
2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block size info\n",
__func__);
return retval;
}
batohs(&fwu->block_size, &(buf[0]));
if (fwu->bl_version == BL_V5) {
fwu->off.flash_cmd = fwu->off.payload + fwu->block_size;
fwu->off.flash_status = fwu->off.flash_cmd;
} else if (fwu->bl_version == BL_V6) {
fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET;
fwu->off.flash_status = V6_FLASH_STATUS_OFFSET;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + fwu->off.properties,
fwu->flash_properties.data,
sizeof(fwu->flash_properties.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash properties\n",
__func__);
return retval;
}
count = 4;
if (fwu->flash_properties.has_pm_config)
count += 2;
if (fwu->flash_properties.has_bl_config)
count += 2;
if (fwu->flash_properties.has_disp_config)
count += 2;
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + fwu->off.block_count,
buf,
count);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block count info\n",
__func__);
return retval;
}
batohs(&fwu->blkcount.ui_firmware, &(buf[0]));
batohs(&fwu->blkcount.ui_config, &(buf[2]));
count = 4;
if (fwu->flash_properties.has_pm_config) {
batohs(&fwu->blkcount.pm_config, &(buf[count]));
count += 2;
}
if (fwu->flash_properties.has_bl_config) {
batohs(&fwu->blkcount.bl_config, &(buf[count]));
count += 2;
}
if (fwu->flash_properties.has_disp_config)
batohs(&fwu->blkcount.dp_config, &(buf[count]));
fwu->has_guest_code = false;
if (fwu->flash_properties.has_query4) {
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + fwu->off.properties_2,
properties_2.data,
sizeof(properties_2.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash properties 2\n",
__func__);
return retval;
}
if (properties_2.has_guest_code) {
retval = synaptics_rmi4_reg_read(rmi4_data,
query_base + fwu->off.gc_block_count,
buf,
2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read guest code block count\n",
__func__);
return retval;
}
batohs(&fwu->blkcount.guest_code, &(buf[0]));
fwu->has_guest_code = true;
}
}
fwu->has_utility_param = false;
return 0;
}
static int fwu_read_f34_queries(void)
{
int retval;
memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount));
memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr));
if (fwu->bl_version == BL_V7)
retval = fwu_read_f34_v7_queries();
else
retval = fwu_read_f34_v5v6_queries();
return retval;
}
static int fwu_write_f34_v7_blocks(unsigned char *block_ptr,
unsigned short block_cnt, unsigned char command)
{
int retval;
unsigned char data_base;
unsigned char length[2];
unsigned short transfer;
unsigned short remaining = block_cnt;
unsigned short block_number = 0;
unsigned short left_bytes;
unsigned short write_size;
unsigned short max_write_size;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
retval = fwu_write_f34_partition_id(command);
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.block_number,
(unsigned char *)&block_number,
sizeof(block_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block number\n",
__func__);
return retval;
}
do {
if (remaining / fwu->payload_length)
transfer = fwu->payload_length;
else
transfer = remaining;
length[0] = (unsigned char)(transfer & MASK_8BIT);
length[1] = (unsigned char)(transfer >> 8);
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.transfer_length,
length,
sizeof(length));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write transfer length (remaining = %d)\n",
__func__, remaining);
return retval;
}
retval = fwu_write_f34_command(command);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write command (remaining = %d)\n",
__func__, remaining);
return retval;
}
#ifdef MAX_WRITE_SIZE
max_write_size = MAX_WRITE_SIZE;
if (max_write_size >= transfer * fwu->block_size)
max_write_size = transfer * fwu->block_size;
else if (max_write_size > fwu->block_size)
max_write_size -= max_write_size % fwu->block_size;
else
max_write_size = fwu->block_size;
#else
max_write_size = transfer * fwu->block_size;
#endif
left_bytes = transfer * fwu->block_size;
do {
if (left_bytes / max_write_size)
write_size = max_write_size;
else
write_size = left_bytes;
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.payload,
block_ptr,
write_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block data (remaining = %d)\n",
__func__, remaining);
return retval;
}
block_ptr += write_size;
left_bytes -= write_size;
} while (left_bytes);
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status (remaining = %d)\n",
__func__, remaining);
return retval;
}
remaining -= transfer;
} while (remaining);
return 0;
}
static int fwu_write_f34_v5v6_blocks(unsigned char *block_ptr,
unsigned short block_cnt, unsigned char command)
{
int retval;
unsigned char data_base;
unsigned char block_number[] = {0, 0};
unsigned short blk;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
block_number[1] |= (fwu->config_area << 5);
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.block_number,
block_number,
sizeof(block_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block number\n",
__func__);
return retval;
}
for (blk = 0; blk < block_cnt; blk++) {
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.payload,
block_ptr,
fwu->block_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block data (block %d)\n",
__func__, blk);
return retval;
}
retval = fwu_write_f34_command(command);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write command for block %d\n",
__func__, blk);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status (block %d)\n",
__func__, blk);
return retval;
}
block_ptr += fwu->block_size;
}
return 0;
}
static int fwu_write_f34_blocks(unsigned char *block_ptr,
unsigned short block_cnt, unsigned char cmd)
{
int retval;
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd);
else
retval = fwu_write_f34_v5v6_blocks(block_ptr, block_cnt, cmd);
return retval;
}
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_read_f34_v7_blocks(unsigned short block_cnt,
unsigned char command)
{
int retval;
unsigned char data_base;
unsigned char length[2];
unsigned short transfer;
unsigned short remaining = block_cnt;
unsigned short block_number = 0;
unsigned short index = 0;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
retval = fwu_write_f34_partition_id(command);
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.block_number,
(unsigned char *)&block_number,
sizeof(block_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block number\n",
__func__);
return retval;
}
do {
if (remaining / fwu->payload_length)
transfer = fwu->payload_length;
else
transfer = remaining;
length[0] = (unsigned char)(transfer & MASK_8BIT);
length[1] = (unsigned char)(transfer >> 8);
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.transfer_length,
length,
sizeof(length));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write transfer length (remaining = %d)\n",
__func__, remaining);
return retval;
}
retval = fwu_write_f34_command(command);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write command (remaining = %d)\n",
__func__, remaining);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status (remaining = %d)\n",
__func__, remaining);
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
data_base + fwu->off.payload,
&fwu->read_config_buf[index],
transfer * fwu->block_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block data (remaining = %d)\n",
__func__, remaining);
return retval;
}
index += (transfer * fwu->block_size);
remaining -= transfer;
} while (remaining);
return 0;
}
static int fwu_read_f34_v5v6_blocks(unsigned short block_cnt,
unsigned char command)
{
int retval;
unsigned char data_base;
unsigned char block_number[] = {0, 0};
unsigned short blk;
unsigned short index = 0;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f34_fd.data_base_addr;
block_number[1] |= (fwu->config_area << 5);
retval = synaptics_rmi4_reg_write(rmi4_data,
data_base + fwu->off.block_number,
block_number,
sizeof(block_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write block number\n",
__func__);
return retval;
}
for (blk = 0; blk < block_cnt; blk++) {
retval = fwu_write_f34_command(command);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write read config command\n",
__func__);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status\n",
__func__);
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
data_base + fwu->off.payload,
&fwu->read_config_buf[index],
fwu->block_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block data (block %d)\n",
__func__, blk);
return retval;
}
index += fwu->block_size;
}
return 0;
}
static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd)
{
int retval;
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
retval = fwu_read_f34_v7_blocks(block_cnt, cmd);
else
retval = fwu_read_f34_v5v6_blocks(block_cnt, cmd);
return retval;
}
#endif
static int fwu_get_image_firmware_id(unsigned int *fw_id)
{
int retval;
char *firmware_id;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (fwu->img.contains_firmware_id) {
*fw_id = fwu->img.firmware_id;
} else {
size_t index, max_index;
unsigned char *strptr = strnstr(fwu->image_name, "PR", MAX_IMAGE_NAME_LEN);
if (!strptr) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: No valid PR number (PRxxxxxxx) found in image file name (%s)\n",
__func__, fwu->image_name);
return -EINVAL;
}
strptr += 2;
firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN + 1, GFP_KERNEL);
if (!firmware_id) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for firmware_id\n",
__func__);
return -ENOMEM;
}
max_index = min((ptrdiff_t)(MAX_FIRMWARE_ID_LEN - 1),
&fwu->image_name[MAX_IMAGE_NAME_LEN] - strptr);
index = 0;
while (index < max_index && isdigit(strptr[index])) {
firmware_id[index] = strptr[index];
index++;
}
retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id);
kfree(firmware_id);
if (retval) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to obtain image firmware ID\n",
__func__);
return -EINVAL;
}
}
return 0;
}
static int fwu_get_device_config_id(void)
{
int retval;
unsigned char config_id_size;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
config_id_size = V7_CONFIG_ID_SIZE;
else
config_id_size = V5V6_CONFIG_ID_SIZE;
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.ctrl_base_addr,
fwu->config_id,
config_id_size);
if (retval < 0)
return retval;
return 0;
}
static enum flash_area fwu_go_nogo(void)
{
int retval;
enum flash_area flash_area = NONE;
unsigned char ii;
unsigned char config_id_size;
unsigned int device_fw_id;
unsigned int image_fw_id;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (fwu->force_update) {
flash_area = UI_FIRMWARE;
goto exit;
}
/* Update both UI and config if device is in bootloader mode */
if (fwu->bl_mode_device) {
flash_area = UI_FIRMWARE;
goto exit;
}
/* Get device firmware ID */
device_fw_id = rmi4_data->firmware_id;
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device firmware ID = %d\n",
__func__, device_fw_id);
/* Get image firmware ID */
retval = fwu_get_image_firmware_id(&image_fw_id);
if (retval < 0) {
flash_area = NONE;
goto exit;
}
dev_info(rmi4_data->pdev->dev.parent,
"%s: Image firmware ID = %d\n",
__func__, image_fw_id);
if (image_fw_id != device_fw_id) {
flash_area = UI_FIRMWARE;
goto exit;
} else if (image_fw_id == device_fw_id) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Image firmware ID is the same as device firmware ID\n",
__func__);
flash_area = NONE;
goto exit;
}
/* Get device config ID */
retval = fwu_get_device_config_id();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read device config ID\n",
__func__);
flash_area = NONE;
goto exit;
}
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
config_id_size = V7_CONFIG_ID_SIZE;
else
config_id_size = V5V6_CONFIG_ID_SIZE;
for (ii = 0; ii < config_id_size; ii++) {
if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) {
flash_area = UI_CONFIG;
goto exit;
} else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) {
flash_area = NONE;
goto exit;
}
}
flash_area = NONE;
exit:
if (flash_area == NONE) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: No need to do reflash\n",
__func__);
} else {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Updating %s\n",
__func__,
flash_area == UI_FIRMWARE ?
"UI firmware and config" :
"UI config only");
}
return flash_area;
}
static int fwu_scan_pdt(void)
{
int retval;
unsigned char ii;
unsigned char intr_count = 0;
unsigned char intr_off;
unsigned char intr_src;
unsigned short addr;
bool f01found = false;
bool f34found = false;
bool f35found = false;
struct synaptics_rmi4_fn_desc rmi_fd;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
fwu->in_ub_mode = false;
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
(unsigned char *)&rmi_fd,
sizeof(rmi_fd));
if (retval < 0)
return retval;
if (rmi_fd.fn_number) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Found F%02x\n",
__func__, rmi_fd.fn_number);
switch (rmi_fd.fn_number) {
case SYNAPTICS_RMI4_F01:
f01found = true;
rmi4_data->f01_query_base_addr =
rmi_fd.query_base_addr;
rmi4_data->f01_ctrl_base_addr =
rmi_fd.ctrl_base_addr;
rmi4_data->f01_data_base_addr =
rmi_fd.data_base_addr;
rmi4_data->f01_cmd_base_addr =
rmi_fd.cmd_base_addr;
break;
case SYNAPTICS_RMI4_F34:
f34found = true;
fwu->f34_fd.query_base_addr =
rmi_fd.query_base_addr;
fwu->f34_fd.ctrl_base_addr =
rmi_fd.ctrl_base_addr;
fwu->f34_fd.data_base_addr =
rmi_fd.data_base_addr;
switch (rmi_fd.fn_version) {
case F34_V0:
fwu->bl_version = BL_V5;
break;
case F34_V1:
fwu->bl_version = BL_V6;
break;
case F34_V2:
fwu->bl_version = BL_V7;
break;
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Unrecognized F34 version\n",
__func__);
return -EINVAL;
}
fwu->intr_mask = 0;
intr_src = rmi_fd.intr_src_count;
intr_off = intr_count % 8;
for (ii = intr_off;
ii < (intr_src + intr_off);
ii++) {
fwu->intr_mask |= 1 << ii;
}
break;
case SYNAPTICS_RMI4_F35:
f35found = true;
fwu->f35_fd.query_base_addr =
rmi_fd.query_base_addr;
fwu->f35_fd.ctrl_base_addr =
rmi_fd.ctrl_base_addr;
fwu->f35_fd.data_base_addr =
rmi_fd.data_base_addr;
fwu->f35_fd.cmd_base_addr =
rmi_fd.cmd_base_addr;
break;
}
} else {
break;
}
intr_count += rmi_fd.intr_src_count;
}
if (!f01found || !f34found) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find both F01 and F34\n",
__func__);
if (!f35found) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find F35\n",
__func__);
return -EINVAL;
} else {
fwu->in_ub_mode = true;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: In microbootloader mode\n",
__func__);
fwu_recovery_check_status();
return 0;
}
}
rmi4_data->intr_mask[0] |= fwu->intr_mask;
addr = rmi4_data->f01_ctrl_base_addr + 1;
retval = synaptics_rmi4_reg_write(rmi4_data,
addr,
&(rmi4_data->intr_mask[0]),
sizeof(rmi4_data->intr_mask[0]));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set interrupt enable bit\n",
__func__);
return retval;
}
return 0;
}
static int fwu_enter_flash_prog(void)
{
int retval;
struct f01_device_control f01_device_control;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_read_flash_status();
if (retval < 0)
return retval;
if (fwu->in_bl_mode)
return 0;
retval = rmi4_data->irq_enable(rmi4_data, false, true);
if (retval < 0)
return retval;
msleep(INT_DISABLE_WAIT_MS);
retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG);
if (retval < 0)
return retval;
retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false);
if (retval < 0)
return retval;
if (!fwu->in_bl_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: BL mode not entered\n",
__func__);
return -EINVAL;
}
if (rmi4_data->hw_if->bl_hw_init) {
retval = rmi4_data->hw_if->bl_hw_init(rmi4_data);
if (retval < 0)
return retval;
}
retval = fwu_scan_pdt();
if (retval < 0)
return retval;
retval = fwu_read_f34_queries();
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
f01_device_control.data,
sizeof(f01_device_control.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F01 device control\n",
__func__);
return retval;
}
f01_device_control.nosleep = true;
f01_device_control.sleep_mode = SLEEP_MODE_NORMAL;
retval = synaptics_rmi4_reg_write(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
f01_device_control.data,
sizeof(f01_device_control.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write F01 device control\n",
__func__);
return retval;
}
msleep(ENTER_FLASH_PROG_WAIT_MS);
return retval;
}
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_check_ui_firmware_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.ui_firmware.size / fwu->block_size;
if (block_count != fwu->blkcount.ui_firmware) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: UI firmware size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
#endif
static int fwu_check_ui_configuration_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.ui_config.size / fwu->block_size;
if (block_count != fwu->blkcount.ui_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: UI configuration size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_check_dp_configuration_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.dp_config.size / fwu->block_size;
if (block_count != fwu->blkcount.dp_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Display configuration size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
static int fwu_check_pm_configuration_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.pm_config.size / fwu->block_size;
if (block_count != fwu->blkcount.pm_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Permanent configuration size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
#endif
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_check_bl_configuration_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.bl_config.size / fwu->block_size;
if (block_count != fwu->blkcount.bl_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Bootloader configuration size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_check_guest_code_size(void)
{
unsigned short block_count;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->img.guest_code.size / fwu->block_size;
if (block_count != fwu->blkcount.guest_code) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Guest code size mismatch\n",
__func__);
return -EINVAL;
}
return 0;
}
#endif
static int fwu_erase_configuration(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
switch (fwu->config_area) {
case UI_CONFIG_AREA:
retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG);
if (retval < 0)
return retval;
break;
case DP_CONFIG_AREA:
retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG);
if (retval < 0)
return retval;
break;
case BL_CONFIG_AREA:
retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG);
if (retval < 0)
return retval;
break;
case FLASH_CONFIG_AREA:
retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG);
if (retval < 0)
return retval;
break;
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid config area\n",
__func__);
return -EINVAL;
}
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
return retval;
}
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_erase_bootloader(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_write_f34_command(CMD_ERASE_BOOTLOADER);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
return 0;
}
static int fwu_erase_utility_parameter(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_write_f34_command(CMD_ERASE_UTILITY_PARAMETER);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_erase_guest_code(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_write_f34_command(CMD_ERASE_GUEST_CODE);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
return 0;
}
#endif
static int fwu_erase_all(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (fwu->bl_version == BL_V7) {
retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
} else {
retval = fwu_write_f34_command(CMD_ERASE_ALL);
if (retval < 0)
return retval;
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Erase all command written\n",
__func__);
retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
if (!(fwu->bl_version == BL_V8 &&
fwu->flash_status == BAD_PARTITION_TABLE)) {
if (retval < 0)
return retval;
}
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Idle status detected\n",
__func__);
if (fwu->bl_version == BL_V8)
return 0;
}
#ifndef SYNA_SIMPLE_UPDATE
if (fwu->flash_properties.has_disp_config &&
fwu->img.contains_disp_config) {
fwu->config_area = DP_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
}
if (fwu->has_guest_code /*&& fwu->img.contains_guest_code*/) {
retval = fwu_erase_guest_code();
if (retval < 0)
return retval;
}
#endif
return 0;
}
static int fwu_write_firmware(void)
{
unsigned short firmware_block_count;
firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size;
return fwu_write_f34_blocks((unsigned char *)fwu->img.ui_firmware.data,
firmware_block_count, CMD_WRITE_FW);
}
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_write_bootloader(void)
{
int retval;
unsigned short bootloader_block_count;
bootloader_block_count = fwu->img.bl_image.size / fwu->block_size;
fwu->write_bootloader = true;
retval = fwu_write_f34_blocks((unsigned char *)fwu->img.bl_image.data,
bootloader_block_count, CMD_WRITE_BOOTLOADER);
fwu->write_bootloader = false;
return retval;
}
static int fwu_write_utility_parameter(void)
{
int retval;
unsigned char ii;
unsigned char checksum_array[4];
unsigned char *pbuf;
unsigned short remaining_size;
unsigned short utility_param_size;
unsigned long checksum;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
utility_param_size = fwu->blkcount.utility_param * fwu->block_size;
/* See remaining_size below for reason for '4' */
if (utility_param_size < 4)
return -EINVAL;
retval = fwu_allocate_read_config_buf(utility_param_size);
if (retval < 0)
return retval;
memset(fwu->read_config_buf, 0x00, utility_param_size);
pbuf = fwu->read_config_buf;
remaining_size = utility_param_size - 4;
for (ii = 0; ii < MAX_UTILITY_PARAMS; ii++) {
if (fwu->img.utility_param_id[ii] == UNUSED)
continue;
#ifdef F51_DISCRETE_FORCE
if (fwu->img.utility_param_id[ii] == FORCE_PARAMETER) {
if (fwu->bl_mode_device) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device in bootloader mode, skipping calibration data restoration\n",
__func__);
goto image_param;
}
retval = secure_memcpy(&(pbuf[4]),
remaining_size - 4,
fwu->cal_data,
fwu->cal_data_buf_size,
fwu->cal_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy force calibration data\n",
__func__);
return retval;
}
pbuf[0] = FORCE_PARAMETER;
pbuf[1] = 0x00;
pbuf[2] = (4 + fwu->cal_data_size) / 2;
pbuf += (fwu->cal_data_size + 4);
remaining_size -= (fwu->cal_data_size + 4);
continue;
}
image_param:
#endif
retval = secure_memcpy(pbuf,
remaining_size,
fwu->img.utility_param[ii].data,
fwu->img.utility_param[ii].size,
fwu->img.utility_param[ii].size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy utility parameter data\n",
__func__);
return retval;
}
pbuf += fwu->img.utility_param[ii].size;
remaining_size -= fwu->img.utility_param[ii].size;
}
calculate_checksum((unsigned short *)fwu->read_config_buf,
((utility_param_size - 4) / 2),
&checksum);
convert_to_little_endian(checksum_array, checksum);
fwu->read_config_buf[utility_param_size - 4] = checksum_array[0];
fwu->read_config_buf[utility_param_size - 3] = checksum_array[1];
fwu->read_config_buf[utility_param_size - 2] = checksum_array[2];
fwu->read_config_buf[utility_param_size - 1] = checksum_array[3];
retval = fwu_write_f34_blocks((unsigned char *)fwu->read_config_buf,
fwu->blkcount.utility_param, CMD_WRITE_UTILITY_PARAM);
if (retval < 0)
return retval;
return 0;
}
#endif
static int fwu_write_configuration(void)
{
return fwu_write_f34_blocks((unsigned char *)fwu->config_data,
fwu->config_block_count, CMD_WRITE_CONFIG);
}
static int fwu_write_ui_configuration(void)
{
fwu->config_area = UI_CONFIG_AREA;
fwu->config_data = fwu->img.ui_config.data;
fwu->config_size = fwu->img.ui_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
return fwu_write_configuration();
}
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_write_dp_configuration(void)
{
fwu->config_area = DP_CONFIG_AREA;
fwu->config_data = fwu->img.dp_config.data;
fwu->config_size = fwu->img.dp_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
return fwu_write_configuration();
}
static int fwu_write_pm_configuration(void)
{
fwu->config_area = PM_CONFIG_AREA;
fwu->config_data = fwu->img.pm_config.data;
fwu->config_size = fwu->img.pm_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
return fwu_write_configuration();
}
#endif
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_write_flash_configuration(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
fwu->config_area = FLASH_CONFIG_AREA;
fwu->config_data = fwu->img.fl_config.data;
fwu->config_size = fwu->img.fl_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
if (fwu->config_block_count != fwu->blkcount.fl_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Flash configuration size mismatch\n",
__func__);
return -EINVAL;
}
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
retval = fwu_write_configuration();
if (retval < 0)
return retval;
rmi4_data->reset_device(rmi4_data, false);
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_write_guest_code(void)
{
int retval;
unsigned short guest_code_block_count;
guest_code_block_count = fwu->img.guest_code.size / fwu->block_size;
retval = fwu_write_f34_blocks((unsigned char *)fwu->img.guest_code.data,
guest_code_block_count, CMD_WRITE_GUEST_CODE);
if (retval < 0)
return retval;
return 0;
}
#endif
static int fwu_write_lockdown(void)
{
unsigned short lockdown_block_count;
lockdown_block_count = fwu->img.lockdown.size / fwu->block_size;
return fwu_write_f34_blocks((unsigned char *)fwu->img.lockdown.data,
lockdown_block_count, CMD_WRITE_LOCKDOWN);
}
#ifndef SYNA_SIMPLE_UPDATE
static int fwu_write_partition_table_v8(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
fwu->config_area = FLASH_CONFIG_AREA;
fwu->config_data = fwu->img.fl_config.data;
fwu->config_size = fwu->img.fl_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
if (fwu->config_block_count != fwu->blkcount.fl_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Flash configuration size mismatch\n",
__func__);
return -EINVAL;
}
retval = fwu_write_configuration();
if (retval < 0)
return retval;
rmi4_data->reset_device(rmi4_data, false);
return 0;
}
static int fwu_write_partition_table_v7(void)
{
int retval;
unsigned short block_count;
block_count = fwu->blkcount.bl_config;
fwu->config_area = BL_CONFIG_AREA;
fwu->config_size = fwu->block_size * block_count;
retval = fwu_allocate_read_config_buf(fwu->config_size);
if (retval < 0)
return retval;
retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG);
if (retval < 0)
return retval;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
retval = fwu_write_flash_configuration();
if (retval < 0)
return retval;
fwu->config_area = BL_CONFIG_AREA;
fwu->config_data = fwu->read_config_buf;
fwu->config_size = fwu->img.bl_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
retval = fwu_write_configuration();
if (retval < 0)
return retval;
return 0;
}
static int fwu_write_bl_area_v7(void)
{
int retval;
bool has_utility_param;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
has_utility_param = fwu->has_utility_param;
if (fwu->has_utility_param) {
retval = fwu_erase_utility_parameter();
if (retval < 0)
return retval;
}
fwu->config_area = BL_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
fwu->config_area = FLASH_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
retval = fwu_erase_bootloader();
if (retval < 0)
return retval;
retval = fwu_write_bootloader();
if (retval < 0)
return retval;
msleep(rmi4_data->hw_if->board_data->reset_delay_ms);
rmi4_data->reset_device(rmi4_data, false);
fwu->config_area = FLASH_CONFIG_AREA;
fwu->config_data = fwu->img.fl_config.data;
fwu->config_size = fwu->img.fl_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
retval = fwu_write_configuration();
if (retval < 0)
return retval;
rmi4_data->reset_device(rmi4_data, false);
fwu->config_area = BL_CONFIG_AREA;
fwu->config_data = fwu->img.bl_config.data;
fwu->config_size = fwu->img.bl_config.size;
fwu->config_block_count = fwu->config_size / fwu->block_size;
retval = fwu_write_configuration();
if (retval < 0)
return retval;
if (fwu->img.contains_utility_param) {
retval = fwu_write_utility_parameter();
if (retval < 0)
return retval;
}
return 0;
}
#endif
#ifdef HTC_FEATURE
static int fwu_get_tw_vendor_v7(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
unsigned char base;
unsigned char data[10], mask_data[2];
uint16_t tw_pin_mask = rmi4_data->hw_if->board_data->tw_pin_mask;
base = fwu->f34_fd.data_base_addr;
memcpy(&mask_data, &tw_pin_mask, sizeof(tw_pin_mask));
memset(data, 0x00, sizeof(data));
data[0] = BOOTLOADER_PARTITION;
data[5] = (unsigned char) CMD_V7_SENSOR_ID;
data[6] = data[8] = mask_data[0];
data[7] = data[9] = mask_data[1];
retval = synaptics_rmi4_reg_write(rmi4_data,
base + fwu->off.partition_id,
data,
sizeof(data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write tw vendor pin\n",
__func__);
return -EINVAL;
}
retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false);
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
base + fwu->off.payload,
data,
sizeof(data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tw vendor pin\n",
__func__);
return -EINVAL;
}
rmi4_data->tw_vendor = (data[5] << 8) | data[4];
dev_info(rmi4_data->pdev->dev.parent, " %s: tw_vendor = 0x%x\n",
__func__,
rmi4_data->tw_vendor);
return 0;
}
static int fwu_get_tw_vendor_v5v6(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
uint16_t tw_pin_mask = rmi4_data->hw_if->board_data->tw_pin_mask;
uint8_t data[6];
memcpy(&data, &tw_pin_mask, sizeof(tw_pin_mask));
data[2] = data[0];
data[3] = data[1];
retval = synaptics_rmi4_reg_write(rmi4_data,
fwu->f34_fd.data_base_addr + 1,
data,
4);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write tw vendor pin\n",
__func__);
return -EINVAL;
}
retval = fwu_write_f34_command(CMD_READ_SENSOR_ID);
if (retval < 0)
return retval;
retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false);
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + 1,
data,
sizeof(data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tw vendor pin\n",
__func__);
return -EINVAL;
}
rmi4_data->tw_vendor = (data[5] << 8) | data[4];
dev_info(rmi4_data->pdev->dev.parent, " %s: tw_vendor = 0x%x\n",
__func__,
rmi4_data->tw_vendor);
return 0;
}
static int fwu_get_tw_vendor(void)
{
int retval;
if (fwu->bl_version == BL_V7)
retval = fwu_get_tw_vendor_v7();
else
retval = fwu_get_tw_vendor_v5v6();
return retval;
}
static int crc_comparison_v5v6(uint32_t config_crc)
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
uint8_t data[17];
int retval;
uint32_t flash_crc;
data[0] = fwu->config_block_count-1;
data[1] = 0x00;
retval = synaptics_rmi4_reg_write(rmi4_data,
fwu->f34_fd.data_base_addr,
data,
2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write crc addr\n",
__func__);
return retval;
}
retval = fwu_write_f34_command(CMD_READ_CONFIG);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write read config command\n",
__func__);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status\n",
__func__);
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + 1,
data,
sizeof(data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read crc data\n",
__func__);
return retval;
}
memcpy(&flash_crc, &data[12], 4);
dev_info(rmi4_data->pdev->dev.parent, " %s: config_crc = %X, flash_crc = %X\n",
__func__, config_crc, flash_crc);
if (flash_crc == config_crc)
return 0;
else
return 1;
}
static int crc_comparison_v7(uint32_t config_crc)
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
uint8_t data[16];
uint8_t length[2];
unsigned char base;
int retval;
uint32_t flash_crc;
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_write_f34_partition_id(CMD_READ_CONFIG);
if (retval < 0)
return retval;
base = fwu->f34_fd.data_base_addr;
data[0] = fwu->config_block_count-1;
data[1] = 0x00;
retval = synaptics_rmi4_reg_write(rmi4_data,
base + fwu->off.block_number,
data,
2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write crc addr\n",
__func__);
return retval;
}
if (fwu->bl_version == BL_V7) {
length[0] = 1;
length[1] = 0;
retval = synaptics_rmi4_reg_write(rmi4_data,
base + fwu->off.transfer_length,
length,
sizeof(length));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write transfer length\n",
__func__);
return retval;
}
}
retval = fwu_write_f34_command(CMD_READ_CONFIG);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write read config command\n",
__func__);
return retval;
}
retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to wait for idle status\n",
__func__);
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
base + fwu->off.payload,
data,
sizeof(data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read crc data\n",
__func__);
return retval;
}
memcpy(&flash_crc, &data[12], 4);
dev_info(rmi4_data->pdev->dev.parent, " %s: config_crc = %X, flash_crc = %X\n",
__func__, config_crc, flash_crc);
if (flash_crc == config_crc)
return 0;
else
return 1;
}
static int crc_comparison(uint32_t config_crc)
{
int retval;
if (fwu->bl_version == BL_V7)
retval = crc_comparison_v7(config_crc);
else
retval = crc_comparison_v5v6(config_crc);
return retval;
}
#endif
static int fwu_do_reflash(void)
{
int retval;
#ifndef SYNA_SIMPLE_UPDATE
bool do_bl_update = false;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!fwu->new_partition_table) {
retval = fwu_check_ui_firmware_size();
if (retval < 0)
return retval;
retval = fwu_check_ui_configuration_size();
if (retval < 0)
return retval;
if (fwu->flash_properties.has_disp_config &&
fwu->img.contains_disp_config) {
retval = fwu_check_dp_configuration_size();
if (retval < 0)
return retval;
}
if (fwu->has_guest_code && fwu->img.contains_guest_code) {
retval = fwu_check_guest_code_size();
if (retval < 0)
return retval;
}
} else if (fwu->bl_version == BL_V7) {
retval = fwu_check_bl_configuration_size();
if (retval < 0)
return retval;
}
if (!fwu->has_utility_param && fwu->img.contains_utility_param) {
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
do_bl_update = true;
}
if (fwu->has_utility_param && !fwu->img.contains_utility_param) {
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
do_bl_update = true;
}
if (!do_bl_update && fwu->incompatible_partition_tables) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Incompatible partition tables\n",
__func__);
return -EINVAL;
} else if (!do_bl_update && fwu->new_partition_table) {
if (!fwu->force_update) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Partition table mismatch\n",
__func__);
return -EINVAL;
}
}
#endif
retval = fwu_erase_all();
if (retval < 0)
return retval;
#ifndef SYNA_SIMPLE_UPDATE
if (do_bl_update) {
retval = fwu_write_bl_area_v7();
if (retval < 0)
return retval;
pr_notice("%s: Bootloader area programmed\n", __func__);
} else if (fwu->bl_version == BL_V7 && fwu->new_partition_table) {
retval = fwu_write_partition_table_v7();
if (retval < 0)
return retval;
pr_notice("%s: Partition table programmed\n", __func__);
} else if (fwu->bl_version == BL_V8) {
retval = fwu_write_partition_table_v8();
if (retval < 0)
return retval;
pr_notice("%s: Partition table programmed\n", __func__);
}
#endif
retval = fwu_write_firmware();
if (retval < 0)
return retval;
pr_notice("%s: Firmware programmed\n", __func__);
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_write_ui_configuration();
if (retval < 0)
return retval;
pr_notice("%s: Configuration programmed\n", __func__);
#ifndef SYNA_SIMPLE_UPDATE
if (fwu->flash_properties.has_disp_config &&
fwu->img.contains_disp_config) {
retval = fwu_write_dp_configuration();
if (retval < 0)
return retval;
pr_notice("%s: Display configuration programmed\n", __func__);
}
if (fwu->has_guest_code && fwu->img.contains_guest_code) {
retval = fwu_write_guest_code();
if (retval < 0)
return retval;
pr_notice("%s: Guest code programmed\n", __func__);
}
#endif
return retval;
}
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_do_read_config(void)
{
int retval;
unsigned short block_count;
unsigned short config_area;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
switch (fwu->config_area) {
case UI_CONFIG_AREA:
block_count = fwu->blkcount.ui_config;
break;
case DP_CONFIG_AREA:
if (!fwu->flash_properties.has_disp_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Display configuration not supported\n",
__func__);
return -EINVAL;
}
block_count = fwu->blkcount.dp_config;
break;
case PM_CONFIG_AREA:
if (!fwu->flash_properties.has_pm_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Permanent configuration not supported\n",
__func__);
return -EINVAL;
}
block_count = fwu->blkcount.pm_config;
break;
case BL_CONFIG_AREA:
if (!fwu->flash_properties.has_bl_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Bootloader configuration not supported\n",
__func__);
return -EINVAL;
}
block_count = fwu->blkcount.bl_config;
break;
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid config area\n",
__func__);
return -EINVAL;
}
if (block_count == 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Invalid block count\n",
__func__);
return -EINVAL;
}
mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6) {
config_area = fwu->config_area;
retval = fwu_enter_flash_prog();
fwu->config_area = config_area;
if (retval < 0)
goto exit;
}
fwu->config_size = fwu->block_size * block_count;
retval = fwu_allocate_read_config_buf(fwu->config_size);
if (retval < 0)
goto exit;
retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG);
exit:
if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6)
rmi4_data->reset_device(rmi4_data, false);
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
return retval;
}
#endif
static int fwu_do_lockdown_v7(void)
{
int retval;
struct f34_v7_data0 status;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_enter_flash_prog();
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + fwu->off.flash_status,
status.data,
sizeof(status.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash status\n",
__func__);
return retval;
}
if (status.device_cfg_status == 2) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device already locked down\n",
__func__);
return 0;
}
retval = fwu_write_lockdown();
if (retval < 0)
return retval;
pr_notice("%s: Lockdown programmed\n", __func__);
return retval;
}
static int fwu_do_lockdown_v5v6(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_enter_flash_prog();
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.query_base_addr + fwu->off.properties,
fwu->flash_properties.data,
sizeof(fwu->flash_properties.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash properties\n",
__func__);
return retval;
}
if (fwu->flash_properties.unlocked == 0) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device already locked down\n",
__func__);
return 0;
}
retval = fwu_write_lockdown();
if (retval < 0)
return retval;
pr_notice("%s: Lockdown programmed\n", __func__);
return retval;
}
#ifdef F51_DISCRETE_FORCE
static int fwu_do_restore_f51_cal_data(void)
{
int retval;
unsigned char checksum_array[4];
unsigned short block_count;
unsigned long checksum;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
block_count = fwu->blkcount.ui_config;
fwu->config_size = fwu->block_size * block_count;
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_allocate_read_config_buf(fwu->config_size);
if (retval < 0)
return retval;
retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG);
if (retval < 0)
return retval;
retval = secure_memcpy(&fwu->read_config_buf[fwu->cal_data_off],
fwu->cal_data_size, fwu->cal_data,
fwu->cal_data_buf_size, fwu->cal_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to restore calibration data\n",
__func__);
return retval;
}
calculate_checksum((unsigned short *)fwu->read_config_buf,
((fwu->config_size - 4) / 2),
&checksum);
convert_to_little_endian(checksum_array, checksum);
fwu->read_config_buf[fwu->config_size - 4] = checksum_array[0];
fwu->read_config_buf[fwu->config_size - 3] = checksum_array[1];
fwu->read_config_buf[fwu->config_size - 2] = checksum_array[2];
fwu->read_config_buf[fwu->config_size - 1] = checksum_array[3];
retval = fwu_enter_flash_prog();
if (retval < 0)
return retval;
fwu->config_area = UI_CONFIG_AREA;
fwu->config_data = fwu->read_config_buf;
fwu->config_block_count = fwu->config_size / fwu->block_size;
retval = fwu_erase_configuration();
if (retval < 0)
return retval;
retval = fwu_write_configuration();
if (retval < 0)
return retval;
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_start_write_guest_code(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_parse_image_info();
if (retval < 0)
return -EINVAL;
if (!fwu->has_guest_code) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Guest code not supported\n",
__func__);
return -EINVAL;
}
if (!fwu->img.contains_guest_code) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: No guest code in firmware image\n",
__func__);
return -EINVAL;
}
if (rmi4_data->sensor_sleep) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Sensor sleeping\n",
__func__);
return -ENODEV;
}
rmi4_data->stay_awake = true;
mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
pr_notice("%s: Start of write guest code process\n", __func__);
retval = fwu_enter_flash_prog();
if (retval < 0)
goto exit;
retval = fwu_check_guest_code_size();
if (retval < 0)
goto exit;
retval = fwu_erase_guest_code();
if (retval < 0)
goto exit;
retval = fwu_write_guest_code();
if (retval < 0)
goto exit;
pr_notice("%s: Guest code programmed\n", __func__);
exit:
rmi4_data->reset_device(rmi4_data, false);
pr_notice("%s: End of write guest code process\n", __func__);
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
rmi4_data->stay_awake = false;
return retval;
}
static int fwu_start_write_config(void)
{
int retval;
unsigned short config_area;
unsigned int device_fw_id;
unsigned int image_fw_id;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = fwu_parse_image_info();
if (retval < 0)
return -EINVAL;
switch (fwu->config_area) {
case UI_CONFIG_AREA:
device_fw_id = rmi4_data->firmware_id;
retval = fwu_get_image_firmware_id(&image_fw_id);
if (retval < 0)
return retval;
if (device_fw_id != image_fw_id) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Device and image firmware IDs don't match\n",
__func__);
return -EINVAL;
}
retval = fwu_check_ui_configuration_size();
if (retval < 0)
return retval;
break;
case DP_CONFIG_AREA:
if (!fwu->flash_properties.has_disp_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Display configuration not supported\n",
__func__);
return -EINVAL;
}
if (!fwu->img.contains_disp_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: No display configuration in firmware image\n",
__func__);
return -EINVAL;
}
retval = fwu_check_dp_configuration_size();
if (retval < 0)
return retval;
break;
case PM_CONFIG_AREA:
if (!fwu->flash_properties.has_pm_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Permanent configuration not supported\n",
__func__);
return -EINVAL;
}
if (!fwu->img.contains_perm_config) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: No permanent configuration in firmware image\n",
__func__);
return -EINVAL;
}
retval = fwu_check_pm_configuration_size();
if (retval < 0)
return retval;
break;
default:
dev_err(rmi4_data->pdev->dev.parent,
"%s: Configuration not supported\n",
__func__);
return -EINVAL;
}
if (rmi4_data->sensor_sleep) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Sensor sleeping\n",
__func__);
return -ENODEV;
}
rmi4_data->stay_awake = true;
mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
pr_notice("%s: Start of write config process\n", __func__);
config_area = fwu->config_area;
retval = fwu_enter_flash_prog();
if (retval < 0)
goto exit;
fwu->config_area = config_area;
if (fwu->config_area != PM_CONFIG_AREA) {
retval = fwu_erase_configuration();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to erase config\n",
__func__);
goto exit;
}
}
switch (fwu->config_area) {
case UI_CONFIG_AREA:
retval = fwu_write_ui_configuration();
if (retval < 0)
goto exit;
break;
case DP_CONFIG_AREA:
retval = fwu_write_dp_configuration();
if (retval < 0)
goto exit;
break;
case PM_CONFIG_AREA:
retval = fwu_write_pm_configuration();
if (retval < 0)
goto exit;
break;
}
pr_notice("%s: Config written\n", __func__);
exit:
switch (fwu->config_area) {
case UI_CONFIG_AREA:
rmi4_data->reset_device(rmi4_data, true);
break;
case DP_CONFIG_AREA:
case PM_CONFIG_AREA:
rmi4_data->reset_device(rmi4_data, false);
break;
}
pr_notice("%s: End of write config process\n", __func__);
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
rmi4_data->stay_awake = false;
return retval;
}
#endif
static int fwu_start_reflash(void)
{
int retval = 0;
enum flash_area flash_area;
const struct firmware *fw_entry = NULL;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (rmi4_data->sensor_sleep) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Sensor sleeping\n",
__func__);
return -ENODEV;
}
rmi4_data->stay_awake = true;
mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
pr_notice("%s: Start of reflash process\n", __func__);
if (fwu->image == NULL) {
if(fwu->bootloader_id[1] == 7 && fwu->bootloader_id[0] == 7)
retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
FW_IMAGE_NAME_BL77, sizeof(FW_IMAGE_NAME_BL77),
sizeof(FW_IMAGE_NAME_BL77));
else
retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
FW_IMAGE_NAME_BL71, sizeof(FW_IMAGE_NAME_BL71),
sizeof(FW_IMAGE_NAME_BL71));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy image file name\n",
__func__);
goto exit;
}
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Requesting firmware image %s\n",
__func__, fwu->image_name);
retval = request_firmware(&fw_entry, fwu->image_name,
rmi4_data->pdev->dev.parent);
if (retval != 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Firmware image %s not available\n",
__func__, fwu->image_name);
retval = -EINVAL;
goto exit;
}
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Firmware image size = %d\n",
__func__, (unsigned int)fw_entry->size);
fwu->image = fw_entry->data;
fwu->image_size = fw_entry->size;
}
retval = fwu_parse_image_info();
if (retval < 0)
goto exit;
#ifndef SYNA_SIMPLE_UPDATE
if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Flash size mismatch\n",
__func__);
if (!fwu->force_update) {
retval = -EINVAL;
goto exit;
}
}
#endif
#ifdef HTC_FEATURE
if (fwu->bootloader_id[1] != fwu->img.bootloader.data[0] ||
fwu->bootloader_id[0] != fwu->img.bootloader.data[1]) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Bootloader version mismatch (chip: %d-%d, img: %d-%d)\n",
__func__,
fwu->bootloader_id[1],
fwu->bootloader_id[0],
fwu->img.bootloader.data[0],
fwu->img.bootloader.data[1]);
#else
if (fwu->bl_version != fwu->img.bl_version) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Bootloader version mismatch\n",
__func__);
#endif
retval = -EINVAL;
goto exit;
}
retval = fwu_read_flash_status();
if (retval < 0)
goto exit;
if (fwu->in_bl_mode) {
fwu->bl_mode_device = true;
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device in bootloader mode\n",
__func__);
} else {
fwu->bl_mode_device = false;
}
flash_area = fwu_go_nogo();
if (flash_area != NONE) {
retval = fwu_enter_flash_prog();
if (retval < 0) {
rmi4_data->reset_device(rmi4_data, false);
goto exit;
}
}
#ifdef F51_DISCRETE_FORCE
if (flash_area != NONE && !fwu->bl_mode_device) {
fwu->config_size = fwu->block_size * fwu->blkcount.ui_config;
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_allocate_read_config_buf(fwu->config_size);
if (retval < 0) {
rmi4_data->reset_device(rmi4_data, false);
goto exit;
}
retval = fwu_read_f34_blocks(fwu->blkcount.ui_config,
CMD_READ_CONFIG);
if (retval < 0) {
rmi4_data->reset_device(rmi4_data, false);
goto exit;
}
retval = secure_memcpy(fwu->cal_data, fwu->cal_data_buf_size,
&fwu->read_config_buf[fwu->cal_data_off],
fwu->cal_data_size, fwu->cal_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to save calibration data\n",
__func__);
rmi4_data->reset_device(rmi4_data, false);
goto exit;
}
}
#endif
switch (flash_area) {
case UI_FIRMWARE:
retval = fwu_do_reflash();
rmi4_data->reset_device(rmi4_data, false);
#ifdef F51_DISCRETE_FORCE
if (retval < 0)
break;
if (fwu->has_utility_param || fwu->img.contains_utility_param)
break;
rmi4_data->reset_device(rmi4_data, false);
if (fwu->bl_mode_device || fwu->in_bl_mode) {
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device in bootloader mode, skipping calibration data restoration\n",
__func__);
break;
}
retval = fwu_do_restore_f51_cal_data();
#endif
break;
case UI_CONFIG:
retval = fwu_check_ui_configuration_size();
if (retval < 0)
break;
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0)
break;
retval = fwu_write_ui_configuration();
rmi4_data->reset_device(rmi4_data, false);
#ifdef F51_DISCRETE_FORCE
if (retval < 0)
break;
if (fwu->has_utility_param)
break;
retval = fwu_do_restore_f51_cal_data();
#endif
break;
case NONE:
default:
rmi4_data->reset_device(rmi4_data, false);
break;
}
if (retval < 0) {
rmi4_data->reset_device(rmi4_data, false);
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do reflash\n",
__func__);
goto exit;
}
if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) {
switch (fwu->bl_version) {
case BL_V5:
case BL_V6:
retval = fwu_do_lockdown_v5v6();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do lockdown\n",
__func__);
}
rmi4_data->reset_device(rmi4_data, false);
break;
case BL_V7:
case BL_V8:
retval = fwu_do_lockdown_v7();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do lockdown\n",
__func__);
}
rmi4_data->reset_device(rmi4_data, false);
break;
default:
break;
}
}
exit:
if (fw_entry)
release_firmware(fw_entry);
pr_notice("%s: End of reflash process\n", __func__);
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
rmi4_data->stay_awake = false;
return retval;
}
static int fwu_recovery_check_status(void)
{
int retval;
unsigned char data_base;
unsigned char status;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f35_fd.data_base_addr;
retval = synaptics_rmi4_reg_read(rmi4_data,
data_base + F35_ERROR_CODE_OFFSET,
&status,
1);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read status\n",
__func__);
return retval;
}
status = status & MASK_5BIT;
if (status != 0x00) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Recovery mode status = %d\n",
__func__, status);
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static int fwu_recovery_erase_completion(void)
{
int retval;
unsigned char data_base;
unsigned char command;
unsigned char status;
unsigned int timeout = F35_ERASE_ALL_WAIT_MS / 20;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
data_base = fwu->f35_fd.data_base_addr;
do {
command = 0x01;
retval = synaptics_rmi4_reg_write(rmi4_data,
fwu->f35_fd.cmd_base_addr,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to issue command\n",
__func__);
return retval;
}
do {
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f35_fd.cmd_base_addr,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read command status\n",
__func__);
return retval;
}
if ((command & 0x01) == 0x00)
break;
msleep(20);
timeout--;
} while (timeout > 0);
if (timeout == 0)
goto exit;
retval = synaptics_rmi4_reg_read(rmi4_data,
data_base + F35_FLASH_STATUS_OFFSET,
&status,
sizeof(status));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash status\n",
__func__);
return retval;
}
if ((status & 0x01) == 0x00)
break;
msleep(20);
timeout--;
} while (timeout > 0);
exit:
if (timeout == 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Timed out waiting for flash erase completion\n",
__func__);
return -ETIMEDOUT;
}
return 0;
}
static int fwu_recovery_erase_all(void)
{
int retval;
unsigned char ctrl_base;
unsigned char command = CMD_F35_ERASE_ALL;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
ctrl_base = fwu->f35_fd.ctrl_base_addr;
retval = synaptics_rmi4_reg_write(rmi4_data,
ctrl_base + F35_CHUNK_COMMAND_OFFSET,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to issue erase all command\n",
__func__);
return retval;
}
if (fwu->f35_fd.cmd_base_addr) {
retval = fwu_recovery_erase_completion();
if (retval < 0)
return retval;
} else {
msleep(F35_ERASE_ALL_WAIT_MS);
}
retval = fwu_recovery_check_status();
if (retval < 0)
return retval;
return 0;
}
static int fwu_recovery_write_chunk(void)
{
int retval;
unsigned char ctrl_base;
unsigned char chunk_number[] = {0, 0};
unsigned char chunk_spare;
unsigned char chunk_size;
unsigned char buf[F35_CHUNK_SIZE + 1];
unsigned short chunk;
unsigned short chunk_total;
unsigned short bytes_written = 0;
unsigned char *chunk_ptr = (unsigned char *)fwu->image;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
ctrl_base = fwu->f35_fd.ctrl_base_addr;
retval = synaptics_rmi4_reg_write(rmi4_data,
ctrl_base + F35_CHUNK_NUM_LSB_OFFSET,
chunk_number,
sizeof(chunk_number));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write chunk number\n",
__func__);
return retval;
}
buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK;
chunk_total = fwu->image_size / F35_CHUNK_SIZE;
chunk_spare = fwu->image_size % F35_CHUNK_SIZE;
if (chunk_spare)
chunk_total++;
for (chunk = 0; chunk < chunk_total; chunk++) {
if (chunk_spare && chunk == chunk_total - 1)
chunk_size = chunk_spare;
else
chunk_size = F35_CHUNK_SIZE;
memset(buf, 0x00, F35_CHUNK_SIZE);
secure_memcpy(buf, sizeof(buf), chunk_ptr,
fwu->image_size - bytes_written,
chunk_size);
retval = synaptics_rmi4_reg_write(rmi4_data,
ctrl_base + F35_CHUNK_DATA_OFFSET,
buf,
sizeof(buf));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write chunk data (chunk %d)\n",
__func__, chunk);
return retval;
}
chunk_ptr += chunk_size;
bytes_written += chunk_size;
}
retval = fwu_recovery_check_status();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write chunk data\n",
__func__);
return retval;
}
return 0;
}
static int fwu_recovery_reset(void)
{
int retval;
unsigned char ctrl_base;
unsigned char command = CMD_F35_RESET;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
ctrl_base = fwu->f35_fd.ctrl_base_addr;
retval = synaptics_rmi4_reg_write(rmi4_data,
ctrl_base + F35_CHUNK_COMMAND_OFFSET,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to issue reset command\n",
__func__);
return retval;
}
msleep(F35_RESET_WAIT_MS);
return 0;
}
static int fwu_start_recovery(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (rmi4_data->sensor_sleep) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Sensor sleeping\n",
__func__);
return -ENODEV;
}
rmi4_data->stay_awake = true;
mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
pr_notice("%s: Start of recovery process\n", __func__);
retval = rmi4_data->irq_enable(rmi4_data, false, false);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to disable interrupt\n",
__func__);
goto exit;
}
retval = fwu_recovery_erase_all();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do erase all in recovery mode\n",
__func__);
goto exit;
}
pr_notice("%s: External flash erased\n", __func__);
retval = fwu_recovery_write_chunk();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write chunk data in recovery mode\n",
__func__);
goto exit;
}
pr_notice("%s: Chunk data programmed\n", __func__);
retval = fwu_recovery_reset();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to reset device in recovery mode\n",
__func__);
goto exit;
}
pr_notice("%s: Recovery mode reset issued\n", __func__);
rmi4_data->reset_device(rmi4_data, true);
retval = 0;
exit:
pr_notice("%s: End of recovery process\n", __func__);
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
rmi4_data->stay_awake = false;
return retval;
}
#endif
#ifdef HTC_FEATURE
static int fwu_do_write_config(uint8_t *config_data)
{
fwu->config_area = UI_CONFIG_AREA;
fwu->config_data = config_data;
fwu->config_size = fwu->blkcount.ui_config * fwu->block_size;
fwu->config_block_count = fwu->blkcount.ui_config;
return fwu_write_configuration();
}
#endif
int synaptics_fw_updater(const unsigned char *fw_data)
{
int retval;
if (!fwu)
return -ENODEV;
if (!fwu->initialized)
return -ENODEV;
if (fwu->in_ub_mode)
return -ENODEV;
fwu->image = fw_data;
retval = fwu_start_reflash();
fwu->image = NULL;
return retval;
}
EXPORT_SYMBOL(synaptics_fw_updater);
#ifdef DO_STARTUP_FW_UPDATE
#ifdef HTC_FEATURE
int synaptics_config_updater(struct synaptics_dsx_board_data *bdata)
{
int retval, i, ii;
struct synaptics_rmi4_config *cfg_table = bdata->config_table;
struct synaptics_rmi4_data *rmi4_data;
unsigned int device_fw_id;
uint16_t tw_pin_mask = bdata->tw_pin_mask;
int config_num = bdata->config_num;
uint32_t crc_checksum;
unsigned char config_id_size;
unsigned char config_id[32];
uint8_t *config_data;
uint32_t config_size;
char str_buf[128];
char tmp_buf[5];
int config_diff = 0;
pr_info("%s\n", __func__);
if (!fwu)
return -ENODEV;
if (!fwu->initialized)
return -ENODEV;
rmi4_data = fwu->rmi4_data;
device_fw_id = rmi4_data->firmware_id;
rmi4_data->stay_awake = true;
memset(config_id, 0, sizeof(config_id));
if (fwu->bl_version == BL_V7)
config_id_size = V7_CONFIG_ID_SIZE;
else
config_id_size = V5V6_CONFIG_ID_SIZE;
/* Get device config ID */
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.ctrl_base_addr,
config_id,
config_id_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read device config ID\n",
__func__);
goto exit;
}
memset(str_buf, 0, sizeof(str_buf));
memset(tmp_buf, 0, sizeof(tmp_buf));
for (ii = 0; ii < config_id_size; ii++) {
snprintf(tmp_buf, 3, "%02x ", config_id[ii]);
strlcat(str_buf, tmp_buf, sizeof(str_buf));
}
dev_info(rmi4_data->pdev->dev.parent,
"%s: Device config ID = %s\n",
__func__,
str_buf);
fwu->config_area = UI_CONFIG_AREA;
fwu->config_size = fwu->blkcount.ui_config * fwu->block_size;
fwu->config_block_count = fwu->blkcount.ui_config;
config_size = fwu->config_size;
pr_notice("%s: Start of write config process\n", __func__);
retval = fwu_enter_flash_prog();
if (retval < 0)
goto exit;
if (tw_pin_mask) {
retval = fwu_get_tw_vendor();
if (retval < 0)
goto exit;
}
i = 0;
while (cfg_table[i].pr_number > device_fw_id) {
i++;
if (i == config_num) {
dev_info(rmi4_data->pdev->dev.parent,
" %s: no config data - pr_number does not match\n",
__func__);
goto exit;
}
}
if (tw_pin_mask) {
while ((cfg_table[i].sensor_id > 0) &&
(cfg_table[i].sensor_id != (rmi4_data->tw_vendor |
SENSOR_ID_CHECKING_EN))) {
i++;
if (i == config_num) {
dev_info(rmi4_data->pdev->dev.parent,
" %s: no config data - sensor_id does not match\n",
__func__);
goto exit;
}
}
}
if (cfg_table[i].sensor_id != (rmi4_data->tw_vendor |
SENSOR_ID_CHECKING_EN)) {
dev_info(rmi4_data->pdev->dev.parent,
" %s: no config data - cross_combination does not match\n",
__func__);
goto exit;
}
config_data = cfg_table[i].config;
/* Get image config ID */
memset(str_buf, 0, sizeof(str_buf));
for (ii = 0; ii < config_id_size; ii++) {
snprintf(tmp_buf, 3, "%02x", config_data[ii]);
strlcat(str_buf, tmp_buf, sizeof(str_buf));
}
dev_info(rmi4_data->pdev->dev.parent,
"%s: Image config ID = %s\n",
__func__,
str_buf);
pr_info("%s: config_size = %d\n", __func__, config_size);
if (config_size > SYN_CONFIG_SIZE)
config_size = SYN_CONFIG_SIZE;
crc_checksum = syn_crc((uint16_t *)config_data, (config_size)/2-2);
if (crc_checksum == 0xFFFFFFFF) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: crc_checksum Error", __func__);
goto exit;
}
memcpy(&config_data[(config_size) - 4], &crc_checksum, 4);
dev_info(rmi4_data->pdev->dev.parent, " %s: crc_cksum = %X\n",
__func__, crc_checksum);
for (ii = 0; ii < config_id_size; ii++) {
if (config_data[ii] != fwu->config_id[ii]) {
config_diff = 1;
break;
}
}
if (!config_diff) {
retval = crc_comparison(crc_checksum);
if (retval < 0) {
dev_info(rmi4_data->pdev->dev.parent, " %s: CRC comparison fail!\n",
__func__);
goto exit;
} else if (retval == 0) {
dev_info(rmi4_data->pdev->dev.parent, " %s: No need to update\n",
__func__);
goto exit;
}
}
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_erase_configuration();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to erase config\n",
__func__);
goto exit;
}
retval = fwu_do_write_config(config_data);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write config\n",
__func__);
}
exit:
rmi4_data->reset_device(rmi4_data, false);
rmi4_data->stay_awake = false;
pr_notice("%s: End of write config process\n", __func__);
dev_info(rmi4_data->pdev->dev.parent, " %s end\n", __func__);
return retval;
}
EXPORT_SYMBOL(synaptics_config_updater);
#endif
static void fwu_startup_fw_update_work(struct work_struct *work)
{
static unsigned char do_once = 1;
#ifdef WAIT_FOR_FB_READY
unsigned int timeout;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
#endif
#ifdef HTC_FEATURE
struct synaptics_dsx_board_data *bdata = fwu->rmi4_data->hw_if->board_data;
unsigned char *fw_source = NULL;
#endif
if (!do_once)
return;
do_once = 0;
#ifdef WAIT_FOR_FB_READY
timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1;
while (!rmi4_data->fb_ready) {
msleep(FB_READY_WAIT_MS);
timeout--;
if (timeout == 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Timed out waiting for FB ready\n",
__func__);
return;
}
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
/* Prevent sysfs operations during initial update. */
mutex_lock(&fwu_sysfs_mutex);
#endif
#ifdef HTC_FEATURE
wake_lock(&fwu->fwu_wake_lock);
if (bdata->update_feature & SYNAPTICS_RMI4_UPDATE_IMAGE)
synaptics_fw_updater(fw_source);
if (bdata->update_feature & SYNAPTICS_RMI4_UPDATE_CONFIG)
synaptics_config_updater(bdata);
wake_unlock(&fwu->fwu_wake_lock);
if (fw_source)
kfree(fw_source);
#else
synaptics_fw_updater(NULL);
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
mutex_unlock(&fwu_sysfs_mutex);
#endif
return;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
if (count < fwu->config_size) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Not enough space (%d bytes) in buffer\n",
__func__, (unsigned int)count);
retval = -EINVAL;
goto show_image_exit;
}
retval = secure_memcpy(buf, count, fwu->read_config_buf,
fwu->read_config_buf_size, fwu->config_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy config data\n",
__func__);
goto show_image_exit;
}
retval = fwu->config_size;
show_image_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
static ssize_t fwu_sysfs_store_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos],
fwu->image_size - fwu->data_pos, buf, count, count);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy image data\n",
__func__);
goto store_image_exit;
}
fwu->data_pos += count;
retval = count;
store_image_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
static ssize_t fwu_sysfs_do_recovery_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
goto do_recovery_store_exit;
}
if (!fwu->in_ub_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Not in microbootloader mode\n",
__func__);
retval = -EINVAL;
goto do_recovery_store_exit;
}
if (!fwu->ext_data_source) {
retval = -EINVAL;
goto do_recovery_store_exit;
}
else
fwu->image = fwu->ext_data_source;
retval = fwu_start_recovery();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do recovery\n",
__func__);
goto free_data_source_recovery_exit;
}
retval = count;
free_data_source_recovery_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->image = NULL;
do_recovery_store_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
goto reflash_store_exit;
}
if (fwu->in_ub_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: In microbootloader mode\n",
__func__);
retval = -EINVAL;
goto reflash_store_exit;
}
//if (!fwu->ext_data_source)
// return -EINVAL;
//else
fwu->image = fwu->ext_data_source;
if (input & LOCKDOWN) {
fwu->do_lockdown = true;
input &= ~LOCKDOWN;
}
if ((input != NORMAL) && (input != FORCE)) {
retval = -EINVAL;
goto reflash_store_exit;
}
if (input == FORCE)
fwu->force_update = true;
retval = synaptics_fw_updater(fwu->image);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to do reflash\n",
__func__);
goto reflash_store_free_exit;
}
retval = count;
reflash_store_free_exit:
if (fwu->ext_data_source != NULL) {
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
}
fwu->image = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = DO_LOCKDOWN;
reflash_store_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
static ssize_t fwu_sysfs_write_config_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
goto write_config_store_exit;
}
if (input != 1) {
retval = -EINVAL;
goto write_config_store_exit;
}
if (fwu->in_ub_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: In microbootloader mode\n",
__func__);
retval = -EINVAL;
goto write_config_store_exit;
}
if (!fwu->ext_data_source) {
retval = -EINVAL;
goto write_config_store_exit;
} else {
fwu->image = fwu->ext_data_source;
}
retval = fwu_start_write_config();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write config\n",
__func__);
goto write_config_store_free_exit;
}
retval = count;
write_config_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->image = NULL;
write_config_store_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
static ssize_t fwu_sysfs_read_config_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (sscanf(buf, "%u", &input) != 1)
return -EINVAL;
if (input != 1)
return -EINVAL;
if (fwu->in_ub_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: In microbootloader mode\n",
__func__);
return -EINVAL;
}
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
retval = fwu_do_read_config();
mutex_unlock(&fwu_sysfs_mutex);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read config\n",
__func__);
return retval;
}
return count;
}
static ssize_t fwu_sysfs_config_area_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long config_area;
retval = sstrtoul(buf, 10, &config_area);
if (retval)
return retval;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
fwu->config_area = config_area;
mutex_unlock(&fwu_sysfs_mutex);
return count;
}
static ssize_t fwu_sysfs_image_name_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
buf, count, count);
mutex_unlock(&fwu_sysfs_mutex);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy image file name\n",
__func__);
return retval;
}
return count;
}
static ssize_t fwu_sysfs_image_size_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long size;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
retval = sstrtoul(buf, 10, &size);
if (retval)
return retval;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
fwu->image_size = size;
fwu->data_pos = 0;
if (fwu->ext_data_source != NULL) {
kfree(fwu->ext_data_source);
}
fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
mutex_unlock(&fwu_sysfs_mutex);
if (!fwu->ext_data_source) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for image data\n",
__func__);
return -ENOMEM;
}
return count;
}
static ssize_t fwu_sysfs_block_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size);
}
static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware);
}
static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config);
}
static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config);
}
static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config);
}
static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config);
}
static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code);
}
static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
if (!mutex_trylock(&fwu_sysfs_mutex))
return -EBUSY;
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
goto write_guest_code_store_exit;
}
if (input != 1) {
retval = -EINVAL;
goto write_guest_code_store_exit;
}
if (fwu->in_ub_mode) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: In microbootloader mode\n",
__func__);
retval = -EINVAL;
goto write_guest_code_store_exit;
}
if (!fwu->ext_data_source) {
retval = -EINVAL;
goto write_guest_code_store_exit;
} else {
fwu->image = fwu->ext_data_source;
}
retval = fwu_start_write_guest_code();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write guest code\n",
__func__);
goto write_guest_code_store_free_exit;
}
retval = count;
write_guest_code_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->image = NULL;
write_guest_code_store_exit:
mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
#endif
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
if (!fwu)
return;
if (fwu->intr_mask & intr_mask)
fwu_read_flash_status();
return;
}
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
unsigned char attr_count;
#endif
struct pdt_properties pdt_props;
if (fwu) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Handle already exists\n",
__func__);
return 0;
}
fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
if (!fwu) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for fwu\n",
__func__);
retval = -ENOMEM;
goto exit;
}
fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL);
if (!fwu->image_name) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for image name\n",
__func__);
retval = -ENOMEM;
goto exit_free_fwu;
}
fwu->rmi4_data = rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
PDT_PROPS,
pdt_props.data,
sizeof(pdt_props.data));
if (retval < 0) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Failed to read PDT properties, assuming 0x00\n",
__func__);
} else if (pdt_props.has_bsr) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Reflash for LTS not currently supported\n",
__func__);
retval = -ENODEV;
goto exit_free_mem;
}
retval = fwu_scan_pdt();
if (retval < 0)
goto exit_free_mem;
if (!fwu->in_ub_mode) {
retval = fwu_read_f34_queries();
if (retval < 0)
goto exit_free_mem;
retval = fwu_get_device_config_id();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read device config ID\n",
__func__);
goto exit_free_mem;
}
}
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = DO_LOCKDOWN;
fwu->initialized = true;
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs bin file\n",
__func__);
goto exit_free_mem;
}
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs attributes\n",
__func__);
retval = -ENODEV;
goto exit_remove_attrs;
}
}
#endif
#ifdef DO_STARTUP_FW_UPDATE
#ifdef HTC_FEATURE
wake_lock_init(&fwu->fwu_wake_lock, WAKE_LOCK_SUSPEND, "fwu_wake_lock");
#endif
fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
INIT_WORK(&fwu->fwu_work, fwu_startup_fw_update_work);
queue_work(fwu->fwu_workqueue,
&fwu->fwu_work);
#endif
#ifdef F51_DISCRETE_FORCE
fwu_read_flash_status();
if (!fwu->in_bl_mode) {
retval = fwu_f51_force_data_init();
if (retval < 0)
goto exit_remove_attrs;
}
#endif
return 0;
#if defined(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26) || \
defined(F51_DISCRETE_FORCE)
exit_remove_attrs:
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
#endif
exit_free_mem:
kfree(fwu->image_name);
exit_free_fwu:
kfree(fwu);
fwu = NULL;
exit:
return retval;
}
static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
{
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
unsigned char attr_count;
#endif
if (!fwu)
goto exit;
#ifdef DO_STARTUP_FW_UPDATE
cancel_work_sync(&fwu->fwu_work);
flush_workqueue(fwu->fwu_workqueue);
destroy_workqueue(fwu->fwu_workqueue);
#ifdef HTC_FEATURE
wake_lock_destroy(&fwu->fwu_wake_lock);
#endif
#endif
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_SYSFS_HTC_v26
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
#endif
#ifdef F51_DISCRETE_FORCE
kfree(fwu->cal_data);
#endif
kfree(fwu->read_config_buf);
kfree(fwu->image_name);
kfree(fwu);
fwu = NULL;
exit:
complete(&fwu_remove_complete);
return;
}
static void synaptics_rmi4_fwu_reset(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
if (!fwu) {
synaptics_rmi4_fwu_init(rmi4_data);
return;
}
retval = fwu_scan_pdt();
if (retval < 0)
return;
if (!fwu->in_ub_mode)
fwu_read_f34_queries();
#ifdef F51_DISCRETE_FORCE
fwu_read_flash_status();
if (!fwu->in_bl_mode)
fwu_f51_force_data_init();
#endif
return;
}
static struct synaptics_rmi4_exp_fn fwu_module = {
.fn_type = RMI_FW_UPDATER,
.init = synaptics_rmi4_fwu_init,
.remove = synaptics_rmi4_fwu_remove,
.reset = synaptics_rmi4_fwu_reset,
.reinit = NULL,
.early_suspend = NULL,
.suspend = NULL,
.resume = NULL,
.late_resume = NULL,
.attn = synaptics_rmi4_fwu_attn,
};
static int __init rmi4_fw_update_module_init(void)
{
synaptics_rmi4_new_function(&fwu_module, true);
return 0;
}
static void __exit rmi4_fw_update_module_exit(void)
{
synaptics_rmi4_new_function(&fwu_module, false);
wait_for_completion(&fwu_remove_complete);
return;
}
module_init(rmi4_fw_update_module_init);
module_exit(rmi4_fw_update_module_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX FW Update Module");
MODULE_LICENSE("GPL v2");