1551 lines
38 KiB
C
Executable file
1551 lines
38 KiB
C
Executable file
/*
|
|
* omnivision TCM touchscreen driver
|
|
*
|
|
* Copyright (C) 2017-2018 omnivision Incorporated. All rights reserved.
|
|
*
|
|
* 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 omnivision
|
|
* 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 omnivision 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 omnivision 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, omnivision'
|
|
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
|
* DOLLARS.
|
|
*/
|
|
|
|
#include <linux/gpio.h>
|
|
#include <linux/crc32.h>
|
|
#include <linux/firmware.h>
|
|
#include "omnivision_tcm_core.h"
|
|
|
|
#define USE_OMNIVSION_IMG_FILE 1
|
|
|
|
#if !USE_OMNIVSION_IMG_FILE
|
|
#include "omnivision_firmware_header.h"
|
|
#endif
|
|
|
|
#define ENABLE_SYS_ZEROFLASH true
|
|
|
|
//#define FW_IMAGE_NAME "omnivision/hdl_firmware.img"
|
|
#define FW_IMAGE_NAME "hdl_firmware.img"
|
|
|
|
#define BOOT_CONFIG_ID "BOOT_CONFIG"
|
|
|
|
#define F35_APP_CODE_ID "F35_APP_CODE"
|
|
|
|
#define ROMBOOT_APP_CODE_ID "ROMBOOT_APP_CODE"
|
|
|
|
#define RESERVED_BYTES 14
|
|
|
|
#define APP_CONFIG_ID "APP_CONFIG"
|
|
|
|
#define DISP_CONFIG_ID "DISPLAY"
|
|
|
|
#define OPEN_SHORT_ID "OPENSHORT"
|
|
|
|
#define SYSFS_DIR_NAME "zeroflash"
|
|
|
|
#define IMAGE_FILE_MAGIC_VALUE 0x4818472b
|
|
|
|
#define FLASH_AREA_MAGIC_VALUE 0x7c05e516
|
|
|
|
#define PDT_START_ADDR 0x00e9
|
|
|
|
#define PDT_END_ADDR 0x00ee
|
|
|
|
#define UBL_FN_NUMBER 0x35
|
|
|
|
#define F35_CTRL3_OFFSET 18
|
|
|
|
#define F35_CTRL7_OFFSET 22
|
|
|
|
#define F35_WRITE_FW_TO_PMEM_COMMAND 4
|
|
|
|
#define TP_RESET_TO_HDL_DELAY_MS 11
|
|
|
|
#define DOWNLOAD_RETRY_COUNT 10
|
|
|
|
enum f35_error_code {
|
|
SUCCESS = 0,
|
|
UNKNOWN_FLASH_PRESENT,
|
|
MAGIC_NUMBER_NOT_PRESENT,
|
|
INVALID_BLOCK_NUMBER,
|
|
BLOCK_NOT_ERASED,
|
|
NO_FLASH_PRESENT,
|
|
CHECKSUM_FAILURE,
|
|
WRITE_FAILURE,
|
|
INVALID_COMMAND,
|
|
IN_DEBUG_MODE,
|
|
INVALID_HEADER,
|
|
REQUESTING_FIRMWARE,
|
|
INVALID_CONFIGURATION,
|
|
DISABLE_BLOCK_PROTECT_FAILURE,
|
|
};
|
|
|
|
enum config_download {
|
|
HDL_INVALID = 0,
|
|
HDL_TOUCH_CONFIG,
|
|
HDL_DISPLAY_CONFIG,
|
|
HDL_OPEN_SHORT_CONFIG,
|
|
};
|
|
|
|
struct area_descriptor {
|
|
unsigned char magic_value[4];
|
|
unsigned char id_string[16];
|
|
unsigned char flags[4];
|
|
unsigned char flash_addr_words[4];
|
|
unsigned char length[4];
|
|
unsigned char checksum[4];
|
|
};
|
|
|
|
struct block_data {
|
|
const unsigned char *data;
|
|
unsigned int size;
|
|
unsigned int flash_addr;
|
|
};
|
|
|
|
struct image_info {
|
|
unsigned int packrat_number;
|
|
struct block_data boot_config;
|
|
struct block_data app_firmware;
|
|
struct block_data app_config;
|
|
struct block_data disp_config;
|
|
struct block_data open_short_config;
|
|
};
|
|
|
|
struct image_header {
|
|
unsigned char magic_value[4];
|
|
unsigned char num_of_areas[4];
|
|
};
|
|
|
|
struct rmi_f35_query {
|
|
unsigned char version:4;
|
|
unsigned char has_debug_mode:1;
|
|
unsigned char has_data5:1;
|
|
unsigned char has_query1:1;
|
|
unsigned char has_query2:1;
|
|
unsigned char chunk_size;
|
|
unsigned char has_ctrl7:1;
|
|
unsigned char has_host_download:1;
|
|
unsigned char has_spi_master:1;
|
|
unsigned char advanced_recovery_mode:1;
|
|
unsigned char reserved:4;
|
|
} __packed;
|
|
|
|
struct rmi_f35_data {
|
|
unsigned char error_code:5;
|
|
unsigned char recovery_mode_forced:1;
|
|
unsigned char nvm_programmed:1;
|
|
unsigned char in_recovery:1;
|
|
} __packed;
|
|
|
|
struct rmi_pdt_entry {
|
|
unsigned char query_base_addr;
|
|
unsigned char command_base_addr;
|
|
unsigned char control_base_addr;
|
|
unsigned char data_base_addr;
|
|
unsigned char intr_src_count:3;
|
|
unsigned char reserved_1:2;
|
|
unsigned char fn_version:2;
|
|
unsigned char reserved_2:1;
|
|
unsigned char fn_number;
|
|
} __packed;
|
|
|
|
struct rmi_addr {
|
|
unsigned short query_base;
|
|
unsigned short command_base;
|
|
unsigned short control_base;
|
|
unsigned short data_base;
|
|
};
|
|
|
|
struct firmware_status {
|
|
unsigned short invalid_static_config:1;
|
|
unsigned short need_disp_config:1;
|
|
unsigned short need_app_config:1;
|
|
unsigned short hdl_version:4;
|
|
unsigned short need_open_short_config:1;
|
|
unsigned short reserved:8;
|
|
} __packed;
|
|
|
|
struct zeroflash_hcd {
|
|
bool has_hdl;
|
|
bool f35_ready;
|
|
bool has_open_short_config;
|
|
const unsigned char *image;
|
|
unsigned char *buf;
|
|
const struct firmware *fw_entry;
|
|
struct work_struct config_work;
|
|
struct workqueue_struct *workqueue;
|
|
struct kobject *sysfs_dir;
|
|
struct rmi_addr f35_addr;
|
|
struct image_info image_info;
|
|
struct firmware_status fw_status;
|
|
struct ovt_tcm_buffer out;
|
|
struct ovt_tcm_buffer resp;
|
|
struct ovt_tcm_hcd *tcm_hcd;
|
|
};
|
|
|
|
static void zeroflash_do_romboot_firmware_download(void);
|
|
|
|
DECLARE_COMPLETION(zeroflash_remove_complete);
|
|
|
|
STORE_PROTOTYPE(zeroflash, hdl)
|
|
|
|
static struct device_attribute *attrs[] = {
|
|
ATTRIFY(hdl),
|
|
};
|
|
|
|
static struct zeroflash_hcd *zeroflash_hcd;
|
|
|
|
static int zeroflash_wait_hdl(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
|
|
msleep(HOST_DOWNLOAD_WAIT_MS);
|
|
|
|
if (!atomic_read(&tcm_hcd->host_downloading))
|
|
return 0;
|
|
|
|
retval = wait_event_interruptible_timeout(tcm_hcd->hdl_wq,
|
|
!atomic_read(&tcm_hcd->host_downloading),
|
|
msecs_to_jiffies(HOST_DOWNLOAD_TIMEOUT_MS));
|
|
if (retval == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Timed out waiting for completion of host download\n");
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
retval = -EIO;
|
|
} else {
|
|
retval = 0;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t zeroflash_sysfs_hdl_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int retval = 0;
|
|
unsigned int input;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
|
|
if (sscanf(buf, "%u", &input) != 1)
|
|
return -EINVAL;
|
|
|
|
if (input && (tcm_hcd->in_hdl_mode)) {
|
|
|
|
retval = tcm_hcd->reset(tcm_hcd);
|
|
if (retval < 0)
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to trigger the host download by reset\n");
|
|
|
|
retval = zeroflash_wait_hdl(tcm_hcd);
|
|
if (retval < 0)
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to wait for completion of host download\n");
|
|
#if USE_OMNIVSION_IMG_FILE
|
|
if (zeroflash_hcd->fw_entry) {
|
|
release_firmware(zeroflash_hcd->fw_entry);
|
|
zeroflash_hcd->fw_entry = NULL;
|
|
}
|
|
#endif
|
|
zeroflash_hcd->image = NULL;
|
|
|
|
} else {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid HDL devices\n");
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int zeroflash_check_uboot(void)
|
|
{
|
|
int retval;
|
|
unsigned char fn_number;
|
|
unsigned int retry = 3;
|
|
struct rmi_f35_query query;
|
|
struct rmi_pdt_entry p_entry;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
|
|
re_check:
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
PDT_END_ADDR,
|
|
&fn_number,
|
|
sizeof(fn_number));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read RMI function number\n");
|
|
return retval;
|
|
}
|
|
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Found F$%02x\n",
|
|
fn_number);
|
|
|
|
if (fn_number != UBL_FN_NUMBER) {
|
|
if (retry--)
|
|
goto re_check;
|
|
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to find F$35\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (zeroflash_hcd->f35_ready)
|
|
return 0;
|
|
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
PDT_START_ADDR,
|
|
(unsigned char *)&p_entry,
|
|
sizeof(p_entry));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read PDT entry\n");
|
|
return retval;
|
|
}
|
|
|
|
zeroflash_hcd->f35_addr.query_base = p_entry.query_base_addr;
|
|
zeroflash_hcd->f35_addr.command_base = p_entry.command_base_addr;
|
|
zeroflash_hcd->f35_addr.control_base = p_entry.control_base_addr;
|
|
zeroflash_hcd->f35_addr.data_base = p_entry.data_base_addr;
|
|
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
zeroflash_hcd->f35_addr.query_base,
|
|
(unsigned char *)&query,
|
|
sizeof(query));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read F$35 query\n");
|
|
return retval;
|
|
}
|
|
|
|
zeroflash_hcd->f35_ready = true;
|
|
|
|
if (query.has_query2 && query.has_ctrl7 && query.has_host_download) {
|
|
zeroflash_hcd->has_hdl = true;
|
|
} else {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Host download not supported\n");
|
|
zeroflash_hcd->has_hdl = false;
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zeroflash_parse_fw_image(void)
|
|
{
|
|
unsigned int idx;
|
|
unsigned int addr;
|
|
unsigned int offset;
|
|
unsigned int length;
|
|
unsigned int checksum;
|
|
unsigned int flash_addr;
|
|
unsigned int magic_value;
|
|
unsigned int num_of_areas;
|
|
struct image_header *header;
|
|
struct image_info *image_info;
|
|
struct area_descriptor *descriptor;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
const unsigned char *image;
|
|
const unsigned char *content;
|
|
|
|
image = zeroflash_hcd->image;
|
|
image_info = &zeroflash_hcd->image_info;
|
|
header = (struct image_header *)image;
|
|
|
|
magic_value = le4_to_uint(header->magic_value);
|
|
if (magic_value != IMAGE_FILE_MAGIC_VALUE) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid image file magic value\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(image_info, 0x00, sizeof(*image_info));
|
|
|
|
offset = sizeof(*header);
|
|
num_of_areas = le4_to_uint(header->num_of_areas);
|
|
|
|
for (idx = 0; idx < num_of_areas; idx++) {
|
|
addr = le4_to_uint(image + offset);
|
|
descriptor = (struct area_descriptor *)(image + addr);
|
|
offset += 4;
|
|
|
|
magic_value = le4_to_uint(descriptor->magic_value);
|
|
if (magic_value != FLASH_AREA_MAGIC_VALUE)
|
|
continue;
|
|
|
|
length = le4_to_uint(descriptor->length);
|
|
content = (unsigned char *)descriptor + sizeof(*descriptor);
|
|
flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2;
|
|
checksum = le4_to_uint(descriptor->checksum);
|
|
|
|
if (0 == strncmp((char *)descriptor->id_string,
|
|
BOOT_CONFIG_ID, strlen(BOOT_CONFIG_ID))) {
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Boot config checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
image_info->boot_config.size = length;
|
|
image_info->boot_config.data = content;
|
|
image_info->boot_config.flash_addr = flash_addr;
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Boot config size = %d\n",
|
|
length);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Boot config flash address = 0x%08x\n",
|
|
flash_addr);
|
|
} else if ((0 == strncmp((char *)descriptor->id_string,
|
|
F35_APP_CODE_ID, strlen(F35_APP_CODE_ID)))) {
|
|
|
|
if (tcm_hcd->sensor_type != TYPE_F35) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Improper descriptor, F35_APP_CODE_ID\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"HDL_F35 firmware checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
image_info->app_firmware.size = length;
|
|
image_info->app_firmware.data = content;
|
|
image_info->app_firmware.flash_addr = flash_addr;
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"HDL_F35 firmware size = %d\n",
|
|
length);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"HDL_F35 firmware flash address = 0x%08x\n",
|
|
flash_addr);
|
|
|
|
} else if ((0 == strncmp((char *)descriptor->id_string,
|
|
ROMBOOT_APP_CODE_ID,
|
|
strlen(ROMBOOT_APP_CODE_ID)))) {
|
|
|
|
if (tcm_hcd->sensor_type != TYPE_ROMBOOT) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Improper descriptor, ROMBOOT_APP_CODE_ID\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"HDL_ROMBoot firmware checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
image_info->app_firmware.size = length;
|
|
image_info->app_firmware.data = content;
|
|
image_info->app_firmware.flash_addr = flash_addr;
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"HDL_ROMBoot firmware size = %d\n",
|
|
length);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"HDL_ROMBoot firmware flash address = 0x%08x\n",
|
|
flash_addr);
|
|
|
|
} else if (0 == strncmp((char *)descriptor->id_string,
|
|
APP_CONFIG_ID, strlen(APP_CONFIG_ID))) {
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Application config checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
image_info->app_config.size = length;
|
|
image_info->app_config.data = content;
|
|
image_info->app_config.flash_addr = flash_addr;
|
|
image_info->packrat_number = le4_to_uint(&content[14]);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Application config size = %d\n",
|
|
length);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Application config flash address = 0x%08x\n",
|
|
flash_addr);
|
|
} else if (0 == strncmp((char *)descriptor->id_string,
|
|
DISP_CONFIG_ID, strlen(DISP_CONFIG_ID))) {
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Display config checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
image_info->disp_config.size = length;
|
|
image_info->disp_config.data = content;
|
|
image_info->disp_config.flash_addr = flash_addr;
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Display config size = %d\n",
|
|
length);
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Display config flash address = 0x%08x\n",
|
|
flash_addr);
|
|
} else if (0 == strncmp((char *)descriptor->id_string,
|
|
OPEN_SHORT_ID, strlen(OPEN_SHORT_ID))) {
|
|
|
|
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"open_short config checksum error\n");
|
|
return -EINVAL;
|
|
}
|
|
zeroflash_hcd->has_open_short_config = true;
|
|
image_info->open_short_config.size = length;
|
|
image_info->open_short_config.data = content;
|
|
image_info->open_short_config.flash_addr = flash_addr;
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"open_short config size = %d\n",
|
|
length);
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"open_short config flash address = 0x%08x\n",
|
|
flash_addr);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zeroflash_get_fw_image(void)
|
|
{
|
|
int retval;
|
|
#if USE_OMNIVSION_IMG_FILE
|
|
int retry_cnt = 10;
|
|
#endif
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
|
|
#if USE_OMNIVSION_IMG_FILE
|
|
if (zeroflash_hcd->fw_entry != NULL) {
|
|
release_firmware(zeroflash_hcd->fw_entry);
|
|
zeroflash_hcd->fw_entry = NULL;
|
|
zeroflash_hcd->image = NULL;
|
|
}
|
|
|
|
while(retry_cnt--) {
|
|
retval = request_firmware(&zeroflash_hcd->fw_entry,
|
|
FW_IMAGE_NAME,
|
|
tcm_hcd->pdev->dev.parent);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to request %s, retry_cnt:%d\n",
|
|
FW_IMAGE_NAME, retry_cnt);
|
|
if (retry_cnt == 0) {
|
|
return retval;
|
|
}
|
|
msleep(1000);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Firmware image size = %d\n",
|
|
(unsigned int)zeroflash_hcd->fw_entry->size);
|
|
|
|
zeroflash_hcd->image = zeroflash_hcd->fw_entry->data;
|
|
#else
|
|
zeroflash_hcd->image = omnivsion_image_array;
|
|
#endif
|
|
|
|
retval = zeroflash_parse_fw_image();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to parse firmware image\n");
|
|
#if USE_OMNIVSION_IMG_FILE
|
|
release_firmware(zeroflash_hcd->fw_entry);
|
|
zeroflash_hcd->fw_entry = NULL;
|
|
#endif
|
|
zeroflash_hcd->image = NULL;
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void zeroflash_download_config(void)
|
|
{
|
|
struct firmware_status *fw_status;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
int retval;
|
|
|
|
fw_status = &zeroflash_hcd->fw_status;
|
|
|
|
if (!fw_status->need_app_config && !fw_status->need_disp_config
|
|
&& !(fw_status->need_open_short_config
|
|
&& zeroflash_hcd->has_open_short_config)
|
|
&& (atomic_read(&tcm_hcd->host_downloading))) {
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
retval = wait_for_completion_timeout(tcm_hcd->helper.helper_completion,
|
|
msecs_to_jiffies(500));
|
|
if (retval == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent, "timeout to wait for helper completion\n");
|
|
return;
|
|
}
|
|
if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) {
|
|
atomic_set(&tcm_hcd->helper.task,
|
|
HELP_SEND_REINIT_NOTIFICATION);
|
|
queue_work(tcm_hcd->helper.workqueue,
|
|
&tcm_hcd->helper.work);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (atomic_read(&tcm_hcd->host_downloading) && !tcm_hcd->ovt_tcm_driver_removing)
|
|
queue_work(zeroflash_hcd->workqueue,
|
|
&zeroflash_hcd->config_work);
|
|
|
|
return;
|
|
}
|
|
|
|
static int zeroflash_download_open_short_config(void)
|
|
{
|
|
int retval;
|
|
unsigned char response_code;
|
|
struct image_info *image_info;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
static unsigned int retry_count;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Downloading open_short config\n");
|
|
|
|
image_info = &zeroflash_hcd->image_info;
|
|
|
|
if (image_info->open_short_config.size == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"No open_short config in image file\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = ovt_tcm_alloc_mem(tcm_hcd,
|
|
&zeroflash_hcd->out,
|
|
image_info->open_short_config.size + 2);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for open_short config\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
switch (zeroflash_hcd->fw_status.hdl_version) {
|
|
case 0:
|
|
case 1:
|
|
zeroflash_hcd->out.buf[0] = 1;
|
|
break;
|
|
case 2:
|
|
zeroflash_hcd->out.buf[0] = 2;
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid HDL version (%d)\n",
|
|
zeroflash_hcd->fw_status.hdl_version);
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.buf[1] = HDL_OPEN_SHORT_CONFIG;
|
|
|
|
retval = secure_memcpy(&zeroflash_hcd->out.buf[2],
|
|
zeroflash_hcd->out.buf_size - 2,
|
|
image_info->open_short_config.data,
|
|
image_info->open_short_config.size,
|
|
image_info->open_short_config.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy open_short config data\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.data_length = image_info->open_short_config.size + 2;
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_DOWNLOAD_CONFIG,
|
|
zeroflash_hcd->out.buf,
|
|
zeroflash_hcd->out.data_length,
|
|
&zeroflash_hcd->resp.buf,
|
|
&zeroflash_hcd->resp.buf_size,
|
|
&zeroflash_hcd->resp.data_length,
|
|
&response_code,
|
|
0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command %s\n",
|
|
STR(CMD_DOWNLOAD_CONFIG));
|
|
if (response_code != STATUS_ERROR)
|
|
goto unlock_resp;
|
|
retry_count++;
|
|
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT)
|
|
goto unlock_resp;
|
|
} else {
|
|
retry_count = 0;
|
|
}
|
|
|
|
retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status,
|
|
sizeof(zeroflash_hcd->fw_status),
|
|
zeroflash_hcd->resp.buf,
|
|
zeroflash_hcd->resp.buf_size,
|
|
sizeof(zeroflash_hcd->fw_status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy firmware status\n");
|
|
goto unlock_resp;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"open_short config downloaded\n");
|
|
|
|
retval = 0;
|
|
|
|
unlock_resp:
|
|
UNLOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
unlock_out:
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int zeroflash_download_disp_config(void)
|
|
{
|
|
int retval;
|
|
unsigned char response_code;
|
|
struct image_info *image_info;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
static unsigned int retry_count;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Downloading display config\n");
|
|
|
|
image_info = &zeroflash_hcd->image_info;
|
|
|
|
if (image_info->disp_config.size == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"No display config in image file\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = ovt_tcm_alloc_mem(tcm_hcd,
|
|
&zeroflash_hcd->out,
|
|
image_info->disp_config.size + 2);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for display config\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
switch (zeroflash_hcd->fw_status.hdl_version) {
|
|
case 0:
|
|
case 1:
|
|
zeroflash_hcd->out.buf[0] = 1;
|
|
break;
|
|
case 2:
|
|
zeroflash_hcd->out.buf[0] = 2;
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid HDL version (%d)\n",
|
|
zeroflash_hcd->fw_status.hdl_version);
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.buf[1] = HDL_DISPLAY_CONFIG;
|
|
|
|
retval = secure_memcpy(&zeroflash_hcd->out.buf[2],
|
|
zeroflash_hcd->out.buf_size - 2,
|
|
image_info->disp_config.data,
|
|
image_info->disp_config.size,
|
|
image_info->disp_config.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy display config data\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.data_length = image_info->disp_config.size + 2;
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_DOWNLOAD_CONFIG,
|
|
zeroflash_hcd->out.buf,
|
|
zeroflash_hcd->out.data_length,
|
|
&zeroflash_hcd->resp.buf,
|
|
&zeroflash_hcd->resp.buf_size,
|
|
&zeroflash_hcd->resp.data_length,
|
|
&response_code,
|
|
0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command %s\n",
|
|
STR(CMD_DOWNLOAD_CONFIG));
|
|
if (response_code != STATUS_ERROR)
|
|
goto unlock_resp;
|
|
retry_count++;
|
|
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT)
|
|
goto unlock_resp;
|
|
} else {
|
|
retry_count = 0;
|
|
}
|
|
|
|
retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status,
|
|
sizeof(zeroflash_hcd->fw_status),
|
|
zeroflash_hcd->resp.buf,
|
|
zeroflash_hcd->resp.buf_size,
|
|
sizeof(zeroflash_hcd->fw_status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy firmware status\n");
|
|
goto unlock_resp;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Display config downloaded\n");
|
|
|
|
retval = 0;
|
|
|
|
unlock_resp:
|
|
UNLOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
unlock_out:
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int zeroflash_download_app_config(void)
|
|
{
|
|
int retval;
|
|
unsigned char padding;
|
|
unsigned char response_code;
|
|
struct image_info *image_info;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
static unsigned int retry_count;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Downloading application config\n");
|
|
|
|
image_info = &zeroflash_hcd->image_info;
|
|
|
|
if (image_info->app_config.size == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"No application config in image file\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
padding = image_info->app_config.size % 8;
|
|
if (padding)
|
|
padding = 8 - padding;
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = ovt_tcm_alloc_mem(tcm_hcd,
|
|
&zeroflash_hcd->out,
|
|
image_info->app_config.size + 2 + padding);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for application config\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
switch (zeroflash_hcd->fw_status.hdl_version) {
|
|
case 0:
|
|
case 1:
|
|
zeroflash_hcd->out.buf[0] = 1;
|
|
break;
|
|
case 2:
|
|
zeroflash_hcd->out.buf[0] = 2;
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid HDL version (%d)\n",
|
|
zeroflash_hcd->fw_status.hdl_version);
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.buf[1] = HDL_TOUCH_CONFIG;
|
|
|
|
retval = secure_memcpy(&zeroflash_hcd->out.buf[2],
|
|
zeroflash_hcd->out.buf_size - 2,
|
|
image_info->app_config.data,
|
|
image_info->app_config.size,
|
|
image_info->app_config.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy application config data\n");
|
|
goto unlock_out;
|
|
}
|
|
|
|
zeroflash_hcd->out.data_length = image_info->app_config.size + 2;
|
|
zeroflash_hcd->out.data_length += padding;
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_DOWNLOAD_CONFIG,
|
|
zeroflash_hcd->out.buf,
|
|
zeroflash_hcd->out.data_length,
|
|
&zeroflash_hcd->resp.buf,
|
|
&zeroflash_hcd->resp.buf_size,
|
|
&zeroflash_hcd->resp.data_length,
|
|
&response_code,
|
|
0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command %s\n",
|
|
STR(CMD_DOWNLOAD_CONFIG));
|
|
if (response_code != STATUS_ERROR)
|
|
goto unlock_resp;
|
|
retry_count++;
|
|
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT)
|
|
goto unlock_resp;
|
|
} else {
|
|
retry_count = 0;
|
|
}
|
|
|
|
retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status,
|
|
sizeof(zeroflash_hcd->fw_status),
|
|
zeroflash_hcd->resp.buf,
|
|
zeroflash_hcd->resp.buf_size,
|
|
sizeof(zeroflash_hcd->fw_status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy firmware status\n");
|
|
goto unlock_resp;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Application config downloaded\n");
|
|
|
|
retval = 0;
|
|
|
|
unlock_resp:
|
|
UNLOCK_BUFFER(zeroflash_hcd->resp);
|
|
|
|
unlock_out:
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void zeroflash_download_config_work(struct work_struct *work)
|
|
{
|
|
int retval;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
|
|
retval = zeroflash_get_fw_image();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get firmware image\n");
|
|
return;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Start of config download\n");
|
|
|
|
if (zeroflash_hcd->fw_status.need_app_config) {
|
|
retval = zeroflash_download_app_config();
|
|
if (retval < 0) {
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to download application config, abort\n");
|
|
return;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
if (zeroflash_hcd->fw_status.need_disp_config) {
|
|
retval = zeroflash_download_disp_config();
|
|
if (retval < 0) {
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to download display config, abort\n");
|
|
return;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
if (zeroflash_hcd->fw_status.need_open_short_config &&
|
|
zeroflash_hcd->has_open_short_config) {
|
|
|
|
retval = zeroflash_download_open_short_config();
|
|
if (retval < 0) {
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to download open_short config, abort\n");
|
|
return;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"End of config download\n");
|
|
|
|
if (tcm_hcd->ovt_tcm_driver_removing == 1) {
|
|
return;
|
|
}
|
|
|
|
zeroflash_download_config();
|
|
|
|
return;
|
|
}
|
|
|
|
static int zeroflash_download_app_fw(void)
|
|
{
|
|
int retval;
|
|
unsigned char command;
|
|
struct image_info *image_info;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
#if TP_RESET_TO_HDL_DELAY_MS
|
|
const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
|
#endif
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Downloading application firmware\n");
|
|
|
|
image_info = &zeroflash_hcd->image_info;
|
|
|
|
if (image_info->app_firmware.size == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"No application firmware in image file\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = ovt_tcm_alloc_mem(tcm_hcd,
|
|
&zeroflash_hcd->out,
|
|
image_info->app_firmware.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for application firmware\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
return retval;
|
|
}
|
|
|
|
retval = secure_memcpy(zeroflash_hcd->out.buf,
|
|
zeroflash_hcd->out.buf_size,
|
|
image_info->app_firmware.data,
|
|
image_info->app_firmware.size,
|
|
image_info->app_firmware.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy application firmware data\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
return retval;
|
|
}
|
|
|
|
zeroflash_hcd->out.data_length = image_info->app_firmware.size;
|
|
|
|
command = F35_WRITE_FW_TO_PMEM_COMMAND;
|
|
|
|
#if TP_RESET_TO_HDL_DELAY_MS
|
|
if (bdata->tpio_reset_gpio >= 0) {
|
|
gpio_set_value(bdata->tpio_reset_gpio, bdata->reset_on_state);
|
|
msleep(bdata->reset_active_ms);
|
|
gpio_set_value(bdata->tpio_reset_gpio, !bdata->reset_on_state);
|
|
mdelay(TP_RESET_TO_HDL_DELAY_MS);
|
|
}
|
|
#endif
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
zeroflash_hcd->f35_addr.control_base + F35_CTRL3_OFFSET,
|
|
&command,
|
|
sizeof(command));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write F$35 command\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
return retval;
|
|
}
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
zeroflash_hcd->f35_addr.control_base + F35_CTRL7_OFFSET,
|
|
zeroflash_hcd->out.buf,
|
|
zeroflash_hcd->out.data_length);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write application firmware data\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
return retval;
|
|
}
|
|
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Application firmware downloaded\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void zeroflash_do_f35_firmware_download(void)
|
|
{
|
|
int retval;
|
|
struct rmi_f35_data data;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
static unsigned int retry_count;
|
|
const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
|
|
|
if (tcm_hcd->irq_enabled) {
|
|
retval = tcm_hcd->enable_irq(tcm_hcd, false, true);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to disable interrupt\n");
|
|
}
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Prepare F35 firmware download\n");
|
|
|
|
if (tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Incorrect uboot type, exit\n");
|
|
goto exit;
|
|
}
|
|
retval = zeroflash_check_uboot();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to find valid uboot\n");
|
|
goto exit;
|
|
}
|
|
|
|
atomic_set(&tcm_hcd->host_downloading, 1);
|
|
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
zeroflash_hcd->f35_addr.data_base,
|
|
(unsigned char *)&data,
|
|
sizeof(data));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read F$35 data\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (data.error_code != REQUESTING_FIRMWARE) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Microbootloader error code = 0x%02x\n",
|
|
data.error_code);
|
|
if (data.error_code != CHECKSUM_FAILURE) {
|
|
retval = -EIO;
|
|
goto exit;
|
|
} else {
|
|
retry_count++;
|
|
}
|
|
} else {
|
|
retry_count = 0;
|
|
}
|
|
|
|
retval = zeroflash_get_fw_image();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get firmware image\n");
|
|
goto exit;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Start of firmware download\n");
|
|
|
|
/* perform firmware downloading */
|
|
retval = zeroflash_download_app_fw();
|
|
if (retval < 0) {
|
|
|
|
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to download application firmware, so reset tp \n");
|
|
goto exit;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"End of firmware download\n");
|
|
|
|
exit:
|
|
if (retval < 0) {
|
|
gpio_set_value(bdata->reset_gpio, 0);
|
|
msleep(5);
|
|
gpio_set_value(bdata->reset_gpio, 1);
|
|
msleep(5);
|
|
retry_count++;
|
|
}
|
|
|
|
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) {
|
|
//retval = tcm_hcd->enable_irq(tcm_hcd, false, true);
|
|
retval = tcm_hcd->enable_irq(tcm_hcd, true, true);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to disable interrupt\n");
|
|
}
|
|
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"Interrupt is disabled\n");
|
|
} else {
|
|
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to enable interrupt\n");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void zeroflash_do_romboot_firmware_download(void)
|
|
{
|
|
int retval;
|
|
unsigned char *resp_buf = NULL;
|
|
unsigned int resp_buf_size;
|
|
unsigned int resp_length;
|
|
unsigned int data_size_blocks;
|
|
unsigned int image_size;
|
|
struct ovt_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
|
const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Prepare ROMBOOT firmware download\n");
|
|
|
|
atomic_set(&tcm_hcd->host_downloading, 1);
|
|
resp_buf = NULL;
|
|
resp_buf_size = 0;
|
|
|
|
if (!tcm_hcd->irq_enabled) {
|
|
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to enable interrupt\n");
|
|
}
|
|
}
|
|
|
|
pm_stay_awake(&tcm_hcd->pdev->dev);
|
|
|
|
if (tcm_hcd->id_info.mode != MODE_ROMBOOTLOADER) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Not in romboot mode\n");
|
|
atomic_set(&tcm_hcd->host_downloading, 0);
|
|
goto exit;
|
|
}
|
|
|
|
retval = zeroflash_get_fw_image();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to request romboot.img\n");
|
|
goto exit;
|
|
}
|
|
|
|
image_size = (unsigned int)zeroflash_hcd->image_info.app_firmware.size;
|
|
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"image_size = %d\n",
|
|
image_size);
|
|
|
|
data_size_blocks = image_size / 16;
|
|
|
|
LOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = ovt_tcm_alloc_mem(tcm_hcd,
|
|
&zeroflash_hcd->out,
|
|
image_size + RESERVED_BYTES);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for application firmware\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
goto exit;
|
|
}
|
|
|
|
zeroflash_hcd->out.buf[0] = zeroflash_hcd->image_info.app_firmware.size >> 16;
|
|
|
|
retval = secure_memcpy(&zeroflash_hcd->out.buf[RESERVED_BYTES],
|
|
zeroflash_hcd->image_info.app_firmware.size,
|
|
zeroflash_hcd->image_info.app_firmware.data,
|
|
zeroflash_hcd->image_info.app_firmware.size,
|
|
zeroflash_hcd->image_info.app_firmware.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy application firmware data\n");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
goto exit;
|
|
}
|
|
|
|
LOGD(tcm_hcd->pdev->dev.parent,
|
|
"data_size_blocks: %d\n",
|
|
data_size_blocks);
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_ROMBOOT_DOWNLOAD,
|
|
zeroflash_hcd->out.buf,
|
|
image_size + RESERVED_BYTES,
|
|
&resp_buf,
|
|
&resp_buf_size,
|
|
&resp_length,
|
|
NULL,
|
|
20);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command ROMBOOT DOWNLOAD");
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
if (tcm_hcd->status_report_code != REPORT_IDENTIFY) {
|
|
gpio_set_value(bdata->reset_gpio, 0);
|
|
msleep(5);
|
|
gpio_set_value(bdata->reset_gpio, 1);
|
|
msleep(5);
|
|
}
|
|
goto exit;
|
|
}
|
|
UNLOCK_BUFFER(zeroflash_hcd->out);
|
|
|
|
retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to switch to bootloader");
|
|
if (tcm_hcd->status_report_code != REPORT_IDENTIFY) {
|
|
gpio_set_value(bdata->reset_gpio, 0);
|
|
msleep(5);
|
|
gpio_set_value(bdata->reset_gpio, 1);
|
|
msleep(5);
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
pm_relax(&tcm_hcd->pdev->dev);
|
|
|
|
kfree(resp_buf);
|
|
|
|
return;
|
|
}
|
|
|
|
static int zeroflash_init(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval = 0;
|
|
int idx;
|
|
|
|
zeroflash_hcd = NULL;
|
|
if (!(tcm_hcd->in_hdl_mode))
|
|
return 0;
|
|
|
|
zeroflash_hcd = kzalloc(sizeof(*zeroflash_hcd), GFP_KERNEL);
|
|
if (!zeroflash_hcd) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for zeroflash_hcd\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
zeroflash_hcd->tcm_hcd = tcm_hcd;
|
|
zeroflash_hcd->image = NULL;
|
|
zeroflash_hcd->has_hdl = false;
|
|
zeroflash_hcd->f35_ready = false;
|
|
zeroflash_hcd->has_open_short_config = false;
|
|
|
|
INIT_BUFFER(zeroflash_hcd->out, false);
|
|
INIT_BUFFER(zeroflash_hcd->resp, false);
|
|
|
|
zeroflash_hcd->workqueue =
|
|
create_singlethread_workqueue("ovt_tcm_zeroflash");
|
|
INIT_WORK(&zeroflash_hcd->config_work,
|
|
zeroflash_download_config_work);
|
|
|
|
if (ENABLE_SYS_ZEROFLASH == false)
|
|
goto init_finished;
|
|
|
|
zeroflash_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME,
|
|
tcm_hcd->sysfs_dir);
|
|
if (!zeroflash_hcd->sysfs_dir) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to create sysfs directory\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
|
//retval = sysfs_create_file(zeroflash_hcd->sysfs_dir,
|
|
retval = sysfs_create_file(&tcm_hcd->pdev->dev.kobj, //default path
|
|
&(*attrs[idx]).attr);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to create sysfs file\n");
|
|
}
|
|
}
|
|
|
|
init_finished:
|
|
/* prepare the firmware download process */
|
|
if (tcm_hcd->in_hdl_mode) {
|
|
switch (tcm_hcd->sensor_type) {
|
|
case TYPE_F35:
|
|
zeroflash_do_f35_firmware_download();
|
|
break;
|
|
case TYPE_ROMBOOT:
|
|
zeroflash_do_romboot_firmware_download();
|
|
break;
|
|
default:
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to find valid HDL state (%d)\n",
|
|
tcm_hcd->sensor_type);
|
|
break;
|
|
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int zeroflash_remove(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int idx;
|
|
|
|
if (!zeroflash_hcd)
|
|
goto exit;
|
|
|
|
if (ENABLE_SYS_ZEROFLASH == true) {
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
|
sysfs_remove_file(zeroflash_hcd->sysfs_dir,
|
|
&(*attrs[idx]).attr);
|
|
}
|
|
|
|
kobject_put(zeroflash_hcd->sysfs_dir);
|
|
}
|
|
|
|
|
|
cancel_work_sync(&zeroflash_hcd->config_work);
|
|
flush_workqueue(zeroflash_hcd->workqueue);
|
|
destroy_workqueue(zeroflash_hcd->workqueue);
|
|
|
|
RELEASE_BUFFER(zeroflash_hcd->resp);
|
|
RELEASE_BUFFER(zeroflash_hcd->out);
|
|
#if USE_OMNIVSION_IMG_FILE
|
|
if (zeroflash_hcd->fw_entry)
|
|
release_firmware(zeroflash_hcd->fw_entry);
|
|
#endif
|
|
kfree(zeroflash_hcd);
|
|
zeroflash_hcd = NULL;
|
|
|
|
exit:
|
|
complete(&zeroflash_remove_complete);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zeroflash_syncbox(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
unsigned char *fw_status;
|
|
|
|
if (!zeroflash_hcd)
|
|
return 0;
|
|
|
|
switch (tcm_hcd->report.id) {
|
|
case REPORT_STATUS:
|
|
fw_status = (unsigned char *)&zeroflash_hcd->fw_status;
|
|
|
|
retval = secure_memcpy(fw_status,
|
|
sizeof(zeroflash_hcd->fw_status),
|
|
tcm_hcd->report.buffer.buf,
|
|
tcm_hcd->report.buffer.buf_size,
|
|
sizeof(zeroflash_hcd->fw_status));
|
|
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy firmware status\n");
|
|
return retval;
|
|
}
|
|
zeroflash_download_config();
|
|
break;
|
|
case REPORT_HDL_F35:
|
|
zeroflash_do_f35_firmware_download();
|
|
break;
|
|
case REPORT_HDL_ROMBOOT:
|
|
zeroflash_do_romboot_firmware_download();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zeroflash_reinit(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
|
|
if (!zeroflash_hcd && tcm_hcd->in_hdl_mode) {
|
|
retval = zeroflash_init(tcm_hcd);
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ovt_tcm_module_cb zeroflash_module = {
|
|
.type = TCM_ZEROFLASH,
|
|
.init = zeroflash_init,
|
|
.remove = zeroflash_remove,
|
|
.syncbox = zeroflash_syncbox,
|
|
#ifdef REPORT_NOTIFIER
|
|
.asyncbox = NULL,
|
|
#endif
|
|
.reinit = zeroflash_reinit,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
.early_suspend = NULL,
|
|
};
|
|
|
|
#ifdef BUILD_AS_KO_MODULE
|
|
|
|
int zeroflash_module_init(void)
|
|
{
|
|
return ovt_tcm_add_module(&zeroflash_module, true);
|
|
}
|
|
|
|
void zeroflash_module_exit(void)
|
|
{
|
|
ovt_tcm_add_module(&zeroflash_module, false);
|
|
|
|
wait_for_completion(&zeroflash_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
EXPORT_SYMBOL(zeroflash_module_init);
|
|
EXPORT_SYMBOL(zeroflash_module_exit);
|
|
|
|
#else
|
|
|
|
static int __init zeroflash_module_init(void)
|
|
{
|
|
return ovt_tcm_add_module(&zeroflash_module, true);
|
|
}
|
|
|
|
static void __exit zeroflash_module_exit(void)
|
|
{
|
|
ovt_tcm_add_module(&zeroflash_module, false);
|
|
|
|
wait_for_completion(&zeroflash_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
module_init(zeroflash_module_init);
|
|
module_exit(zeroflash_module_exit);
|
|
|
|
MODULE_AUTHOR("omnivision, Inc.");
|
|
MODULE_DESCRIPTION("omnivision TCM Zeroflash Module");
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
#endif
|