
Add all drivers for new platforms. Change-Id: Ie9947b0c6f8ddfee7dab6dfa80d6aca62323f4da Signed-off-by: Fei Mao <feim1@codeaurora.org>
5798 行
138 KiB
C
5798 行
138 KiB
C
/*
|
|
* Synaptics DSX touchscreen driver
|
|
*
|
|
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
|
|
*
|
|
* Copyright (c) 2018-2021 The Linux Foundation. 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/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/input.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/input/synaptics_dsx.h>
|
|
#include "synaptics_dsx_core.h"
|
|
|
|
#define FW_IHEX_NAME "synaptics/startup_fw_update.bin"
|
|
#define FW_IMAGE_NAME "synaptics/startup_fw_update.img"
|
|
|
|
#define ENABLE_SYS_REFLASH false
|
|
#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
|
|
#define READ_CONFIG_WAIT_MS 20
|
|
|
|
static int fwu_do_reflash(void);
|
|
|
|
static int fwu_recovery_check_status(void);
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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_utility_parameter_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);
|
|
|
|
#ifdef SYNA_TDDI
|
|
static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count);
|
|
|
|
static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
#endif
|
|
|
|
#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,
|
|
#ifdef SYNA_TDDI
|
|
TDDI_FORCE_CONFIG_AREA,
|
|
TDDI_LCM_DATA_AREA,
|
|
TDDI_OEM_DATA_AREA,
|
|
#endif
|
|
UPP_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,
|
|
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_ERASE_CHIP = 0x0d,
|
|
CMD_V5V6_ENABLE_FLASH_PROG = 0xf,
|
|
#ifdef SYNA_TDDI
|
|
CMD_V5V6_ERASE_FORCE_CONFIG = 0x11,
|
|
CMD_V5V6_READ_FORCE_CONFIG = 0x12,
|
|
CMD_V5V6_WRITE_FORCE_CONFIG = 0x13,
|
|
CMD_V5V6_ERASE_LOCKDOWN_DATA = 0x1a,
|
|
CMD_V5V6_READ_LOCKDOWN_DATA = 0x1b,
|
|
CMD_V5V6_WRITE_LOCKDOWN_DATA = 0x1c,
|
|
CMD_V5V6_ERASE_LCM_DATA = 0x1d,
|
|
CMD_V5V6_ERASE_OEM_DATA = 0x1e,
|
|
#endif
|
|
};
|
|
|
|
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 SYNA_TDDI
|
|
CMD_ERASE_CHIP,
|
|
CMD_ERASE_FORCE_CONFIG,
|
|
CMD_READ_FORCE_CONFIG,
|
|
CMD_WRITE_FORCE_CONFIG,
|
|
CMD_ERASE_LOCKDOWN_DATA,
|
|
CMD_READ_LOCKDOWN_DATA,
|
|
CMD_WRITE_LOCKDOWN_DATA,
|
|
CMD_ERASE_LCM_DATA,
|
|
CMD_READ_LCM_DATA,
|
|
CMD_WRITE_LCM_DATA,
|
|
CMD_ERASE_OEM_DATA,
|
|
CMD_READ_OEM_DATA,
|
|
CMD_WRITE_OEM_DATA,
|
|
#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 f34_query4_b1:1;
|
|
unsigned char has_gesture_config:1;
|
|
unsigned char has_force_config:1;
|
|
unsigned char has_lockdown_data:1;
|
|
unsigned char has_lcm_data:1;
|
|
unsigned char has_oem_data:1;
|
|
unsigned char f34_query4_b7:1;
|
|
} __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;
|
|
#ifdef SYNA_TDDI
|
|
unsigned short tddi_force_config;
|
|
unsigned short tddi_lockdown_data;
|
|
unsigned short tddi_lcm_data;
|
|
unsigned short tddi_oem_data;
|
|
#endif
|
|
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;
|
|
#ifdef SYNA_TDDI
|
|
bool has_force_config;
|
|
bool has_lockdown_data;
|
|
bool has_lcm_data;
|
|
bool has_oem_data;
|
|
#endif
|
|
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 CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
static struct bin_attribute dev_attr_data = {
|
|
.attr = {
|
|
.name = "data",
|
|
.mode = 0664,
|
|
},
|
|
.size = 0,
|
|
.read = fwu_sysfs_show_image,
|
|
.write = fwu_sysfs_store_image,
|
|
};
|
|
#endif
|
|
|
|
static struct device_attribute attrs[] = {
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
__ATTR(dorecovery, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_do_recovery_store),
|
|
__ATTR(doreflash, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_do_reflash_store),
|
|
__ATTR(writeconfig, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_write_config_store),
|
|
__ATTR(readconfig, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_read_config_store),
|
|
__ATTR(configarea, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_config_area_store),
|
|
__ATTR(imagename, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_image_name_store),
|
|
__ATTR(imagesize, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_image_size_store),
|
|
__ATTR(blocksize, 0444,
|
|
fwu_sysfs_block_size_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(fwblockcount, 0444,
|
|
fwu_sysfs_firmware_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(configblockcount, 0444,
|
|
fwu_sysfs_configuration_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(dispconfigblockcount, 0444,
|
|
fwu_sysfs_disp_config_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(permconfigblockcount, 0444,
|
|
fwu_sysfs_perm_config_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(blconfigblockcount, 0444,
|
|
fwu_sysfs_bl_config_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(uppblockcount, 0444,
|
|
fwu_sysfs_utility_parameter_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(guestcodeblockcount, 0444,
|
|
fwu_sysfs_guest_code_block_count_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(writeguestcode, 0220,
|
|
synaptics_rmi4_show_error,
|
|
fwu_sysfs_write_guest_code_store),
|
|
#ifdef SYNA_TDDI
|
|
__ATTR(lockdowncode, 0664,
|
|
fwu_sysfs_read_lockdown_code_show,
|
|
fwu_sysfs_write_lockdown_code_store),
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
static struct synaptics_rmi4_fwu_handle *fwu;
|
|
|
|
DECLARE_COMPLETION(fwu_remove_complete);
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
DEFINE_MUTEX(fwu_sysfs_mutex);
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void fwu_parse_partition_table(const unsigned char *partition_table,
|
|
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;
|
|
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 < 8; 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;
|
|
};
|
|
}
|
|
}
|
|
|
|
static void 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;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
|
|
num_of_containers = fwu->img.utility.size / 4;
|
|
|
|
for (ii = 0; ii < num_of_containers; ii++) {
|
|
if (ii >= MAX_UTILITY_PARAMS)
|
|
continue;
|
|
addr = le_to_uint(fwu->img.utility.data + (ii * 4));
|
|
descriptor = (struct container_descriptor *)(image + addr);
|
|
container_id = descriptor->container_id[0] |
|
|
descriptor->container_id[1] << 8;
|
|
content = image + le_to_uint(descriptor->content_address);
|
|
length = le_to_uint(descriptor->content_length);
|
|
switch (container_id) {
|
|
case UTILITY_PARAMETER_CONTAINER:
|
|
fwu->img.utility_param[ii].data = content;
|
|
fwu->img.utility_param[ii].size = length;
|
|
fwu->img.utility_param_id[ii] = content[0];
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
static void 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;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
|
|
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));
|
|
descriptor = (struct container_descriptor *)(image + addr);
|
|
container_id = descriptor->container_id[0] |
|
|
descriptor->container_id[1] << 8;
|
|
content = image + le_to_uint(descriptor->content_address);
|
|
length = le_to_uint(descriptor->content_length);
|
|
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;
|
|
};
|
|
}
|
|
}
|
|
|
|
static void fwu_parse_image_header_10(void)
|
|
{
|
|
unsigned char ii;
|
|
unsigned char num_of_containers;
|
|
unsigned int addr;
|
|
unsigned int offset;
|
|
unsigned int container_id;
|
|
unsigned int length;
|
|
const unsigned char *image;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
struct image_header_10 *header;
|
|
|
|
image = fwu->image;
|
|
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);
|
|
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++) {
|
|
addr = le_to_uint(image + offset);
|
|
offset += 4;
|
|
descriptor = (struct container_descriptor *)(image + addr);
|
|
container_id = descriptor->container_id[0] |
|
|
descriptor->container_id[1] << 8;
|
|
content = image + le_to_uint(descriptor->content_address);
|
|
length = le_to_uint(descriptor->content_length);
|
|
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;
|
|
fwu_parse_image_header_10_bootloader(image);
|
|
break;
|
|
case UTILITY_CONTAINER:
|
|
fwu->img.utility.data = content;
|
|
fwu->img.utility.size = length;
|
|
fwu_parse_image_header_10_utility(image);
|
|
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;
|
|
fwu->img.firmware_id = le_to_uint(content + 4);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void 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;
|
|
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) {
|
|
fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
|
|
if (fwu->img.contains_bootloader)
|
|
fwu->img.ui_firmware.data += fwu->img.bootloader_size;
|
|
}
|
|
|
|
if ((fwu->img.bl_version == BL_V6) && header->options_tddi)
|
|
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) {
|
|
fwu->img.ui_config.data = fwu->img.ui_firmware.data +
|
|
fwu->img.ui_firmware.size;
|
|
}
|
|
|
|
if (fwu->img.contains_bootloader || 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);
|
|
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;
|
|
|
|
fwu->img.lockdown.size = LOCKDOWN_SIZE;
|
|
fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE;
|
|
}
|
|
|
|
static int fwu_parse_image_info(void)
|
|
{
|
|
struct image_header_10 *header;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
header = (struct image_header_10 *)fwu->image;
|
|
|
|
memset(&fwu->img, 0x00, sizeof(fwu->img));
|
|
|
|
switch (header->major_header_version) {
|
|
case IMAGE_HEADER_VERSION_10:
|
|
fwu_parse_image_header_10();
|
|
break;
|
|
case IMAGE_HEADER_VERSION_05:
|
|
case IMAGE_HEADER_VERSION_06:
|
|
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 (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;
|
|
}
|
|
|
|
fwu_parse_partition_table(fwu->img.fl_config.data,
|
|
&fwu->img.blkcount, &fwu->img.phyaddr);
|
|
|
|
if (fwu->img.blkcount.utility_param)
|
|
fwu->img.contains_utility_param = true;
|
|
|
|
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);
|
|
}
|
|
|
|
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) {
|
|
if (fwu->flash_status == 0x08)
|
|
fwu->flash_status = 0x00;
|
|
}
|
|
|
|
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;
|
|
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 SYNA_TDDI
|
|
case CMD_ERASE_CHIP:
|
|
command = CMD_V5V6_ERASE_CHIP;
|
|
break;
|
|
case CMD_ERASE_FORCE_CONFIG:
|
|
command = CMD_V5V6_ERASE_FORCE_CONFIG;
|
|
break;
|
|
case CMD_READ_FORCE_CONFIG:
|
|
command = CMD_V5V6_READ_FORCE_CONFIG;
|
|
break;
|
|
case CMD_WRITE_FORCE_CONFIG:
|
|
command = CMD_V5V6_WRITE_CONFIG;
|
|
break;
|
|
case CMD_ERASE_LOCKDOWN_DATA:
|
|
command = CMD_V5V6_ERASE_LOCKDOWN_DATA;
|
|
break;
|
|
case CMD_READ_LOCKDOWN_DATA:
|
|
command = CMD_V5V6_READ_LOCKDOWN_DATA;
|
|
break;
|
|
case CMD_WRITE_LOCKDOWN_DATA:
|
|
command = CMD_V5V6_WRITE_LOCKDOWN_DATA;
|
|
break;
|
|
case CMD_ERASE_LCM_DATA:
|
|
command = CMD_V5V6_ERASE_LCM_DATA;
|
|
break;
|
|
case CMD_ERASE_OEM_DATA:
|
|
command = CMD_V5V6_ERASE_OEM_DATA;
|
|
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:
|
|
#ifdef SYNA_TDDI
|
|
case CMD_ERASE_CHIP:
|
|
case CMD_ERASE_FORCE_CONFIG:
|
|
case CMD_ERASE_LOCKDOWN_DATA:
|
|
case CMD_ERASE_LCM_DATA:
|
|
case CMD_ERASE_OEM_DATA:
|
|
#endif
|
|
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;
|
|
else if (fwu->config_area == UPP_AREA)
|
|
partition = UTILITY_PARAMETER_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;
|
|
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;
|
|
}
|
|
|
|
msleep(READ_CONFIG_WAIT_MS);
|
|
|
|
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;
|
|
}
|
|
|
|
fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr);
|
|
|
|
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 base;
|
|
unsigned char offset;
|
|
unsigned char buf[10];
|
|
struct f34_v5v6_flash_properties_2 properties_2;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
base = fwu->f34_fd.query_base_addr;
|
|
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
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,
|
|
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,
|
|
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,
|
|
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;
|
|
#ifdef SYNA_TDDI
|
|
fwu->has_force_config = false;
|
|
fwu->has_lockdown_data = false;
|
|
fwu->has_lcm_data = false;
|
|
fwu->has_oem_data = false;
|
|
#endif
|
|
|
|
if (fwu->flash_properties.has_query4) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
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;
|
|
}
|
|
offset = fwu->off.properties_2 + 1;
|
|
count = 0;
|
|
if (properties_2.has_guest_code) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
base + offset + 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]));
|
|
count++;
|
|
fwu->has_guest_code = true;
|
|
}
|
|
#ifdef SYNA_TDDI
|
|
if (properties_2.has_force_config) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
base + offset + count,
|
|
buf,
|
|
2);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read tddi force block count\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
batohs(&fwu->blkcount.tddi_force_config, &(buf[0]));
|
|
count++;
|
|
fwu->has_force_config = true;
|
|
}
|
|
if (properties_2.has_lockdown_data) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
base + offset + count,
|
|
buf,
|
|
2);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read tddi lockdown block count\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
batohs(&fwu->blkcount.tddi_lockdown_data, &(buf[0]));
|
|
count++;
|
|
fwu->has_lockdown_data = true;
|
|
}
|
|
if (properties_2.has_lcm_data) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
base + offset + count,
|
|
buf,
|
|
2);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read tddi lcm block count\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
batohs(&fwu->blkcount.tddi_lcm_data, &(buf[0]));
|
|
count++;
|
|
fwu->has_lcm_data = true;
|
|
}
|
|
if (properties_2.has_oem_data) {
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
base + offset + count,
|
|
buf,
|
|
2);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read tddi oem block count\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
batohs(&fwu->blkcount.tddi_oem_data, &(buf[0]));
|
|
fwu->has_oem_data = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int fwu_get_image_firmware_id(unsigned int *fw_id)
|
|
{
|
|
int retval;
|
|
unsigned char index = 0;
|
|
char *strptr;
|
|
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 {
|
|
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, GFP_KERNEL);
|
|
if (!firmware_id) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to alloc mem for firmware_id\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
while (strptr[index] >= '0' && strptr[index] <= '9') {
|
|
firmware_id[index] = strptr[index];
|
|
index++;
|
|
if (index == MAX_FIRMWARE_ID_LEN - 1)
|
|
break;
|
|
}
|
|
|
|
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 older than 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
case UPP_AREA:
|
|
retval = fwu_write_f34_command(CMD_ERASE_UTILITY_PARAMETER);
|
|
if (retval < 0)
|
|
return retval;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef SYNA_TDDI
|
|
static int fwu_erase_lockdown_data(void)
|
|
{
|
|
int retval;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
retval = fwu_write_f34_command(CMD_ERASE_LOCKDOWN_DATA);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
|
"%s: Erase command written\n",
|
|
__func__);
|
|
|
|
msleep(100);
|
|
|
|
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_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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (fwu->flash_properties.has_disp_config) {
|
|
fwu->config_area = DP_CONFIG_AREA;
|
|
retval = fwu_erase_configuration();
|
|
if (retval < 0)
|
|
return retval;
|
|
}
|
|
|
|
if (fwu->has_guest_code) {
|
|
retval = fwu_erase_guest_code();
|
|
if (retval < 0)
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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();
|
|
}
|
|
|
|
#ifdef SYNA_TDDI
|
|
static int fwu_write_tddi_lockdown_data(void)
|
|
{
|
|
int retval;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
retval = fwu_write_f34_blocks(fwu->read_config_buf,
|
|
fwu->blkcount.tddi_lockdown_data,
|
|
CMD_WRITE_LOCKDOWN_DATA);
|
|
if (retval < 0)
|
|
return retval;
|
|
rmi4_data->reset_device(rmi4_data, false);
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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) {
|
|
fwu->config_area = UPP_AREA;
|
|
retval = fwu_erase_configuration();
|
|
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;
|
|
}
|
|
|
|
static int fwu_do_reflash(void)
|
|
{
|
|
int retval;
|
|
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;
|
|
}
|
|
}
|
|
|
|
retval = fwu_erase_all();
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
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__);
|
|
}
|
|
|
|
fwu->config_area = UI_CONFIG_AREA;
|
|
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__);
|
|
}
|
|
|
|
retval = fwu_write_ui_configuration();
|
|
if (retval < 0)
|
|
return retval;
|
|
pr_notice("%s: 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__);
|
|
}
|
|
|
|
retval = fwu_write_firmware();
|
|
if (retval < 0)
|
|
return retval;
|
|
pr_notice("%s: Firmware programmed\n", __func__);
|
|
|
|
return retval;
|
|
}
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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;
|
|
case UPP_AREA:
|
|
if (!fwu->has_utility_param) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Utility parameter not supported\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
block_count = fwu->blkcount.utility_param;
|
|
break;
|
|
#ifdef SYNA_TDDI
|
|
case TDDI_FORCE_CONFIG_AREA:
|
|
if (!fwu->has_force_config) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: force configuration not supported\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
block_count = fwu->blkcount.tddi_force_config;
|
|
break;
|
|
case TDDI_OEM_DATA_AREA:
|
|
if (!fwu->has_oem_data) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: oem data not supported\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
block_count = fwu->blkcount.tddi_oem_data;
|
|
break;
|
|
case TDDI_LCM_DATA_AREA:
|
|
if (!fwu->has_lcm_data) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: lcm data not supported\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
block_count = fwu->blkcount.tddi_lcm_data;
|
|
break;
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
#ifdef SYNA_TDDI
|
|
static int fwu_do_read_tddi_lockdown_data(void)
|
|
{
|
|
int retval = -EINVAL;
|
|
unsigned short block_count;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
block_count = fwu->blkcount.tddi_lockdown_data;
|
|
fwu->config_size = fwu->block_size * block_count;
|
|
|
|
if (fwu->bl_version != BL_V6) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Not support lockdown data in bl v.%d\n",
|
|
__func__,
|
|
fwu->bl_version);
|
|
goto exit;
|
|
} else if (!fwu->has_lockdown_data) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Not support lockdown data\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
kfree(fwu->read_config_buf);
|
|
|
|
fwu->read_config_buf = kzalloc(fwu->config_size, 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;
|
|
retval = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
fwu->read_config_buf_size = fwu->config_size;
|
|
retval = fwu_read_f34_blocks(block_count, CMD_READ_LOCKDOWN_DATA);
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
int get_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng)
|
|
{
|
|
int retval;
|
|
|
|
retval = fwu_do_read_tddi_lockdown_data();
|
|
if (retval < 0)
|
|
return retval;
|
|
memcpy(lockdown_data, fwu->read_config_buf, leng);
|
|
return retval;
|
|
}
|
|
|
|
int set_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng)
|
|
{
|
|
int retval = -EINVAL;
|
|
unsigned long checksum;
|
|
unsigned char checksum_array[4];
|
|
unsigned short blk_cnt;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
if (fwu->bl_version != BL_V6) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Not support lockdown data in bl v.%d\n",
|
|
__func__,
|
|
fwu->bl_version);
|
|
goto exit;
|
|
} else if (!fwu->has_lockdown_data) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Not support lockdown data\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
retval = fwu_enter_flash_prog();
|
|
if (retval < 0)
|
|
goto exit;
|
|
|
|
retval = fwu_erase_lockdown_data();
|
|
if (retval < 0)
|
|
goto exit;
|
|
|
|
blk_cnt = fwu->blkcount.tddi_lockdown_data;
|
|
|
|
fwu->config_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size;
|
|
retval = fwu_allocate_read_config_buf(fwu->config_size);
|
|
if (retval < 0)
|
|
goto exit;
|
|
memset(fwu->read_config_buf, 0x00, fwu->config_size);
|
|
retval = secure_memcpy(fwu->read_config_buf, fwu->config_size,
|
|
lockdown_data, leng, leng);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to copy tddi lockdwon data\n",
|
|
__func__);
|
|
goto exit;
|
|
}
|
|
|
|
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[blk_cnt * fwu->block_size - 4] = checksum_array[0];
|
|
fwu->read_config_buf[blk_cnt * fwu->block_size - 3] = checksum_array[1];
|
|
fwu->read_config_buf[blk_cnt * fwu->block_size - 2] = checksum_array[2];
|
|
fwu->read_config_buf[blk_cnt * fwu->block_size - 1] = checksum_array[3];
|
|
retval = fwu_write_tddi_lockdown_data();
|
|
exit:
|
|
return retval;
|
|
}
|
|
#endif
|
|
#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;
|
|
#ifdef SYNA_TDDI
|
|
unsigned char *img_ld;
|
|
|
|
img_ld = (unsigned char *)fwu->img.lockdown.data;
|
|
if (fwu->has_lockdown_data) {
|
|
retval = set_tddi_lockdown_data(img_ld,
|
|
LOCKDOWN_SIZE);
|
|
if (retval < 0)
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to write lockdown data\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
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_EXTRA_SYSFS
|
|
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;
|
|
bool do_rebuild = false;
|
|
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) {
|
|
retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
|
|
FW_IMAGE_NAME, sizeof(FW_IMAGE_NAME),
|
|
sizeof(FW_IMAGE_NAME));
|
|
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;
|
|
}
|
|
|
|
retval = fwu_parse_image_info();
|
|
if (retval < 0)
|
|
goto exit;
|
|
|
|
if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Flash size mismatch\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (fwu->bl_version != fwu->img.bl_version) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Bootloader version mismatch\n",
|
|
__func__);
|
|
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:
|
|
do_rebuild = true;
|
|
retval = fwu_do_reflash();
|
|
#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:
|
|
do_rebuild = true;
|
|
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();
|
|
#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:
|
|
break;
|
|
}
|
|
|
|
if (retval < 0) {
|
|
do_rebuild = false;
|
|
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);
|
|
|
|
if (do_rebuild)
|
|
rmi4_data->reset_device(rmi4_data, true);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
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 recovery process\n", __func__);
|
|
|
|
if (fwu->image == NULL) {
|
|
retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
|
|
FW_IHEX_NAME, sizeof(FW_IHEX_NAME),
|
|
sizeof(FW_IHEX_NAME));
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to copy ihex file name\n",
|
|
__func__);
|
|
goto exit;
|
|
}
|
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
|
"%s: Requesting firmware ihex %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 ihex %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 = 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:
|
|
if (fw_entry)
|
|
release_firmware(fw_entry);
|
|
|
|
pr_notice("%s: End of recovery process\n", __func__);
|
|
|
|
mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
|
|
|
|
rmi4_data->stay_awake = false;
|
|
|
|
return retval;
|
|
}
|
|
|
|
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) {
|
|
fwu->image = NULL;
|
|
retval = fwu_start_recovery();
|
|
if (retval < 0)
|
|
return retval;
|
|
}
|
|
|
|
fwu->image = fw_data;
|
|
|
|
retval = fwu_start_reflash();
|
|
|
|
fwu->image = NULL;
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(synaptics_fw_updater);
|
|
|
|
#ifdef DO_STARTUP_FW_UPDATE
|
|
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
|
|
|
|
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
|
|
|
|
synaptics_fw_updater(NULL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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 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 exit;
|
|
} else {
|
|
retval = fwu->config_size;
|
|
}
|
|
|
|
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 exit;
|
|
} else {
|
|
retval = count;
|
|
}
|
|
|
|
fwu->data_pos += count;
|
|
|
|
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 (kstrtouint(buf, 10, &input) != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!fwu->in_ub_mode) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Not in microbootloader mode\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!fwu->ext_data_source) {
|
|
retval = -EINVAL;
|
|
goto 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 exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
kfree(fwu->ext_data_source);
|
|
fwu->ext_data_source = NULL;
|
|
fwu->image = NULL;
|
|
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 (kstrtouint(buf, 10, &input) != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (fwu->in_ub_mode) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: In microbootloader mode\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!fwu->ext_data_source) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
} else {
|
|
fwu->image = fwu->ext_data_source;
|
|
}
|
|
|
|
if (input & LOCKDOWN) {
|
|
fwu->do_lockdown = true;
|
|
input &= ~LOCKDOWN;
|
|
}
|
|
|
|
if ((input != NORMAL) && (input != FORCE)) {
|
|
retval = -EINVAL;
|
|
goto 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 exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
kfree(fwu->ext_data_source);
|
|
fwu->ext_data_source = NULL;
|
|
fwu->image = NULL;
|
|
fwu->force_update = FORCE_UPDATE;
|
|
fwu->do_lockdown = DO_LOCKDOWN;
|
|
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 (kstrtouint(buf, 10, &input) != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (input != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (fwu->in_ub_mode) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: In microbootloader mode\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!fwu->ext_data_source) {
|
|
retval = -EINVAL;
|
|
goto 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 exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
kfree(fwu->ext_data_source);
|
|
fwu->ext_data_source = NULL;
|
|
fwu->image = NULL;
|
|
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 (kstrtouint(buf, 10, &input) != 1)
|
|
return -EINVAL;
|
|
|
|
if (input != 1)
|
|
return -EINVAL;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
if (fwu->in_ub_mode) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: In microbootloader mode\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
retval = fwu_do_read_config();
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read config\n",
|
|
__func__);
|
|
goto exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to copy image file name\n",
|
|
__func__);
|
|
} else {
|
|
retval = count;
|
|
}
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
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;
|
|
|
|
kfree(fwu->ext_data_source);
|
|
fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
|
|
if (!fwu->ext_data_source) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to alloc mem for image data\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
}
|
|
retval = count;
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_block_size_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_utility_parameter_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.utility_param);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int retval;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code);
|
|
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
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 (kstrtouint(buf, 10, &input) != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (input != 1) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (fwu->in_ub_mode) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: In microbootloader mode\n",
|
|
__func__);
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!fwu->ext_data_source) {
|
|
retval = -EINVAL;
|
|
goto 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 exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
kfree(fwu->ext_data_source);
|
|
fwu->ext_data_source = NULL;
|
|
fwu->image = NULL;
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return retval;
|
|
}
|
|
|
|
#ifdef SYNA_TDDI
|
|
static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
unsigned short lockdown_data_size;
|
|
unsigned char *lockdown_data;
|
|
char ld_val[2];
|
|
int retval = 0;
|
|
int i = 0;
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
lockdown_data_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size;
|
|
lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL);
|
|
if (!lockdown_data) {
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (get_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) {
|
|
kfree(lockdown_data);
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < lockdown_data_size; i++) {
|
|
retval += snprintf(ld_val, PAGE_SIZE, "%02x",
|
|
*(lockdown_data + i));
|
|
strlcat(buf, ld_val, lockdown_data_size);
|
|
}
|
|
*(buf + retval) = '\n';
|
|
kfree(lockdown_data);
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return retval + 1;
|
|
}
|
|
|
|
static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
unsigned short lockdown_data_size = (count - 1) / 2;
|
|
unsigned char *lockdown_data;
|
|
unsigned char temp[2];
|
|
int ld_val;
|
|
int i = 0;
|
|
|
|
for (i = 0; i < (count - 1); i++) {
|
|
if (((*buf >= '0') && (*buf <= '9')) ||
|
|
(('a' < *buf) && (*buf > 'f')) ||
|
|
(('A' < *buf) && (*buf > 'F')))
|
|
continue;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (count % 2 != 1)
|
|
return -EINVAL;
|
|
|
|
lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL);
|
|
if (!lockdown_data)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < lockdown_data_size; i++) {
|
|
memcpy(temp, (buf + 2 * i), sizeof(temp));
|
|
if (kstrtoint(temp, 16, &ld_val) == 1)
|
|
*(lockdown_data + i) = ld_val & 0xff;
|
|
}
|
|
|
|
if (!mutex_trylock(&fwu_sysfs_mutex))
|
|
return -EBUSY;
|
|
|
|
if (set_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) {
|
|
kfree(lockdown_data);
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return -EINVAL;
|
|
}
|
|
kfree(lockdown_data);
|
|
mutex_unlock(&fwu_sysfs_mutex);
|
|
return count;
|
|
}
|
|
#endif
|
|
#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;
|
|
unsigned char attr_count;
|
|
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 DO_STARTUP_FW_UPDATE
|
|
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_free_mem;
|
|
}
|
|
#endif
|
|
|
|
if (ENABLE_SYS_REFLASH == false)
|
|
return 0;
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit_remove_attrs:
|
|
for (attr_count--; attr_count >= 0; attr_count--) {
|
|
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
|
|
&attrs[attr_count].attr);
|
|
}
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
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)
|
|
{
|
|
unsigned char attr_count;
|
|
|
|
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);
|
|
#endif
|
|
|
|
#ifdef F51_DISCRETE_FORCE
|
|
kfree(fwu->cal_data);
|
|
#endif
|
|
kfree(fwu->read_config_buf);
|
|
kfree(fwu->image_name);
|
|
kfree(fwu);
|
|
fwu = NULL;
|
|
|
|
if (ENABLE_SYS_REFLASH == false)
|
|
goto exit;
|
|
|
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
|
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
|
|
&attrs[attr_count].attr);
|
|
}
|
|
|
|
#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
|
|
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
|
|
#endif
|
|
|
|
exit:
|
|
complete(&fwu_remove_complete);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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");
|