1393 lines
30 KiB
C
Executable file
1393 lines
30 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 "omnivision_tcm_core.h"
|
|
|
|
#define SET_UP_RECOVERY_MODE true
|
|
|
|
#define ENABLE_SYSFS_RECOVERY true
|
|
|
|
#define SYSFS_DIR_NAME "recovery"
|
|
|
|
#define IHEX_BUF_SIZE (2048 * 1024)
|
|
|
|
#define DATA_BUF_SIZE (512 * 1024)
|
|
|
|
#define IHEX_RECORD_SIZE 14
|
|
|
|
#define PDT_START_ADDR 0x00e9
|
|
|
|
#define UBL_FN_NUMBER 0x35
|
|
|
|
#define F35_CHUNK_SIZE 16
|
|
|
|
#define F35_CHUNK_SIZE_WORDS 8
|
|
|
|
#define F35_ERASE_ALL_WAIT_MS 5000
|
|
|
|
#define F35_ERASE_ALL_POLL_MS 100
|
|
|
|
#define F35_DATA5_OFFSET 5
|
|
|
|
#define F35_CTRL3_OFFSET 18
|
|
|
|
#define F35_RESET_COMMAND 16
|
|
|
|
#define F35_ERASE_ALL_COMMAND 3
|
|
|
|
#define F35_WRITE_CHUNK_COMMAND 2
|
|
|
|
#define F35_READ_FLASH_STATUS_COMMAND 1
|
|
|
|
#define BINARY_FILE_MAGIC_VALUE 0xaa55
|
|
|
|
#define FLASH_PAGE_SIZE 256
|
|
|
|
#define STATUS_CHECK_US_MIN 5000
|
|
|
|
#define STATUS_CHECK_US_MAX 10000
|
|
|
|
#define STATUS_CHECK_RETRY 50
|
|
|
|
#define DATA_ROMBOOT_BUF_SIZE (512 * 256)
|
|
|
|
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 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 recovery_hcd {
|
|
bool set_up_recovery_mode;
|
|
unsigned char chunk_buf[F35_CHUNK_SIZE + 3];
|
|
unsigned char out_buf[3];
|
|
unsigned char *ihex_buf;
|
|
unsigned char *data_buf;
|
|
unsigned int ihex_size;
|
|
unsigned int ihex_records;
|
|
unsigned int data_entries;
|
|
struct image_info image_info;
|
|
struct kobject *sysfs_dir;
|
|
struct rmi_addr f35_addr;
|
|
struct ovt_tcm_hcd *tcm_hcd;
|
|
};
|
|
|
|
enum flash_command {
|
|
JEDEC_PAGE_PROGRAM = 0x02,
|
|
JEDEC_READ_STATUS = 0x05,
|
|
JEDEC_WRITE_ENABLE = 0x06,
|
|
JEDEC_CHIP_ERASE = 0xc7,
|
|
};
|
|
|
|
struct flash_param {
|
|
unsigned char spi_param;
|
|
unsigned char clk_div;
|
|
unsigned char mode;
|
|
unsigned short read_size;
|
|
unsigned char jedec_cmd;
|
|
} __packed;
|
|
|
|
DECLARE_COMPLETION(recovery_remove_complete);
|
|
|
|
static struct recovery_hcd *recovery_hcd;
|
|
|
|
static void recovery_do_romboot_recovery(struct ovt_tcm_hcd *tcm_hcd);
|
|
|
|
static int recovery_do_f35_recovery(void);
|
|
|
|
static int recovery_add_romboot_data_entry(unsigned char data);
|
|
|
|
STORE_PROTOTYPE(recovery, f35_recovery)
|
|
STORE_PROTOTYPE(recovery, romboot_recovery)
|
|
|
|
|
|
static struct device_attribute *attrs[] = {
|
|
ATTRIFY(f35_recovery),
|
|
ATTRIFY(romboot_recovery),
|
|
};
|
|
|
|
static ssize_t recovery_sysfs_ihex_store(struct file *data_file,
|
|
struct kobject *kobj, struct bin_attribute *attributes,
|
|
char *buf, loff_t pos, size_t count);
|
|
|
|
static struct bin_attribute bin_attr = {
|
|
.attr = {
|
|
.name = "ihex",
|
|
.mode = (S_IWUSR | S_IWGRP),
|
|
},
|
|
.size = 0,
|
|
.write = recovery_sysfs_ihex_store,
|
|
};
|
|
|
|
static ssize_t recovery_sysfs_romboot_recovery_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
unsigned int input;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
if (sscanf(buf, "%u", &input) != 1)
|
|
return -EINVAL;
|
|
|
|
if (input)
|
|
recovery_do_romboot_recovery(tcm_hcd);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int recovery_parse_romboot_ihex(void)
|
|
{
|
|
int retval;
|
|
unsigned char colon;
|
|
unsigned char *buf;
|
|
unsigned int addr;
|
|
unsigned int type;
|
|
unsigned int addrl;
|
|
unsigned int addrh;
|
|
unsigned int data0;
|
|
unsigned int data1;
|
|
unsigned int count;
|
|
unsigned int words;
|
|
unsigned int offset;
|
|
unsigned int record;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
struct image_info *image_info = &recovery_hcd->image_info;
|
|
|
|
if (!(recovery_hcd->ihex_buf)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"No ihex data\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
words = 0;
|
|
offset = 0;
|
|
buf = recovery_hcd->ihex_buf;
|
|
recovery_hcd->data_entries = 0;
|
|
recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE;
|
|
memset(recovery_hcd->data_buf, 0xff, DATA_ROMBOOT_BUF_SIZE);
|
|
|
|
for (record = 0; record < recovery_hcd->ihex_records; record++) {
|
|
buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00;
|
|
retval = sscanf(&buf[record * IHEX_RECORD_SIZE],
|
|
"%c%02x%02x%02x%02x%02x%02x",
|
|
&colon,
|
|
&count,
|
|
&addrh,
|
|
&addrl,
|
|
&type,
|
|
&data0,
|
|
&data1);
|
|
if (retval != 7) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read ihex record\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (type == 0x00) {
|
|
addr = (addrh << 8) + addrl;
|
|
addr += offset;
|
|
|
|
recovery_hcd->data_entries = addr;
|
|
|
|
retval = recovery_add_romboot_data_entry(data0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry for data0\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_add_romboot_data_entry(data1);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry for data1\n");
|
|
return retval;
|
|
}
|
|
|
|
words++;
|
|
} else if (type == 0x02) {
|
|
offset = (data0 << 8) + data1;
|
|
offset <<= 4;
|
|
|
|
recovery_hcd->data_entries = offset;
|
|
}
|
|
}
|
|
|
|
image_info->app_firmware.size = DATA_ROMBOOT_BUF_SIZE;
|
|
image_info->app_firmware.data = recovery_hcd->data_buf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_flash_romboot_command(struct ovt_tcm_hcd *tcm_hcd,
|
|
unsigned char flash_command, unsigned char *out,
|
|
unsigned int out_size, unsigned char *in,
|
|
unsigned int in_size)
|
|
{
|
|
int retval;
|
|
unsigned char *payld_buf = NULL;
|
|
unsigned char *resp_buf = NULL;
|
|
unsigned int resp_buf_size;
|
|
unsigned int resp_length;
|
|
struct flash_param flash_param = {1, 0x19, 0, 0, 0};
|
|
|
|
flash_param.read_size = in_size;
|
|
|
|
flash_param.jedec_cmd = flash_command;
|
|
|
|
resp_buf = NULL;
|
|
resp_buf_size = 0;
|
|
|
|
payld_buf = kzalloc(sizeof(flash_param) + out_size,
|
|
GFP_KERNEL);
|
|
|
|
memcpy(payld_buf, &flash_param, sizeof(flash_param));
|
|
|
|
memcpy(payld_buf + sizeof(flash_param), out, out_size);
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED,
|
|
payld_buf,
|
|
sizeof(flash_param) + out_size,
|
|
&resp_buf,
|
|
&resp_buf_size,
|
|
&resp_length,
|
|
NULL,
|
|
20);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED");
|
|
goto exit;
|
|
}
|
|
|
|
if (in_size && (in_size <= resp_length))
|
|
memcpy(in, resp_buf, in_size);
|
|
|
|
exit:
|
|
kfree(payld_buf);
|
|
kfree(resp_buf);
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_flash_romboot_status(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
int idx;
|
|
unsigned char status;
|
|
|
|
for (idx = 0; idx < STATUS_CHECK_RETRY; idx++) {
|
|
retval = recovery_flash_romboot_command(tcm_hcd,
|
|
JEDEC_READ_STATUS, NULL, 0,
|
|
&status, sizeof(status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write JEDEC_READ_STATUS");
|
|
return retval;
|
|
}
|
|
|
|
usleep_range(STATUS_CHECK_US_MIN, STATUS_CHECK_US_MAX);
|
|
|
|
if (!status)
|
|
break;
|
|
}
|
|
|
|
if (status)
|
|
retval = -EIO;
|
|
else
|
|
retval = status;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_flash_romboot_erase(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"%s", __func__);
|
|
|
|
retval = recovery_flash_romboot_command(tcm_hcd, JEDEC_WRITE_ENABLE,
|
|
NULL, 0, NULL, 0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write JEDEC_WRITE_ENABLE");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_flash_romboot_command(tcm_hcd, JEDEC_CHIP_ERASE,
|
|
NULL, 0, NULL, 0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write JEDEC_CHIP_ERASE");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_flash_romboot_status(tcm_hcd);
|
|
if (retval < 0)
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get correct status: %d", retval);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_flash_romboot_reprogram(struct ovt_tcm_hcd *tcm_hcd,
|
|
unsigned char *image_buf, unsigned int image_size)
|
|
{
|
|
int retval;
|
|
int idx;
|
|
unsigned short img_header;
|
|
unsigned int pages;
|
|
unsigned char buf[FLASH_PAGE_SIZE + 3];
|
|
|
|
img_header = image_buf[0] | image_buf[1] << 8;
|
|
if ((image_size % FLASH_PAGE_SIZE) ||
|
|
(img_header != BINARY_FILE_MAGIC_VALUE)) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Wrong image file");
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"image_size = %d, img_header = 0x%04x",
|
|
image_size, img_header);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pages = image_size / FLASH_PAGE_SIZE;
|
|
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"image_size = %d, img_header = 0x%04x, pages = %d",
|
|
image_size, img_header, pages);
|
|
|
|
for (idx = 0; idx < pages; idx++) {
|
|
retval = recovery_flash_romboot_command(tcm_hcd,
|
|
JEDEC_WRITE_ENABLE, NULL, 0, NULL, 0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write JEDEC_WRITE_ENABLE");
|
|
return retval;
|
|
}
|
|
|
|
buf[0] = FLASH_PAGE_SIZE * idx >> 16;
|
|
buf[1] = FLASH_PAGE_SIZE * idx >> 8;
|
|
buf[2] = FLASH_PAGE_SIZE * idx;
|
|
|
|
memcpy(buf + 3, image_buf + FLASH_PAGE_SIZE * idx,
|
|
FLASH_PAGE_SIZE);
|
|
|
|
retval = recovery_flash_romboot_command(tcm_hcd,
|
|
JEDEC_PAGE_PROGRAM, buf, sizeof(buf), NULL, 0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write JEDEC_READ_STATUS");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_flash_romboot_status(tcm_hcd);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get correct status: %d",
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void recovery_do_romboot_recovery(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
unsigned int image_size;
|
|
unsigned char *out_buf = NULL;
|
|
|
|
unsigned char *resp_buf;
|
|
unsigned int resp_buf_size;
|
|
unsigned int resp_length;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"%s\n", __func__);
|
|
|
|
mutex_lock(&tcm_hcd->extif_mutex);
|
|
|
|
pm_stay_awake(&tcm_hcd->pdev->dev);
|
|
atomic_set(&tcm_hcd->firmware_flashing, 1);
|
|
|
|
if (tcm_hcd->id_info.mode != MODE_ROMBOOTLOADER) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Not in romboot\n");
|
|
if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) {
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_RUN_BOOTLOADER_FIRMWARE,
|
|
NULL,
|
|
0,
|
|
&resp_buf,
|
|
&resp_buf_size,
|
|
&resp_length,
|
|
NULL,
|
|
0);
|
|
if (retval > 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"ok enter bootloader mode\n");
|
|
} else {
|
|
goto do_program_exit;
|
|
}
|
|
}
|
|
|
|
if (tcm_hcd->id_info.mode == MODE_TDDI_BOOTLOADER) {
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
CMD_REBOOT_TO_ROM_BOOTLOADER,
|
|
NULL,
|
|
0,
|
|
&resp_buf,
|
|
&resp_buf_size,
|
|
&resp_length,
|
|
NULL,
|
|
0);
|
|
if (retval > 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"ok enter romboot mode\n");
|
|
} else {
|
|
goto do_program_exit;
|
|
}
|
|
}
|
|
if (tcm_hcd->id_info.mode != MODE_ROMBOOTLOADER) {
|
|
goto do_program_exit;
|
|
}
|
|
}
|
|
|
|
retval = recovery_parse_romboot_ihex();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to parse ihex data\n");
|
|
goto do_program_exit;
|
|
}
|
|
|
|
image_size = (unsigned int)recovery_hcd->image_info.app_firmware.size;
|
|
out_buf = kzalloc(image_size, GFP_KERNEL);
|
|
|
|
retval = secure_memcpy(out_buf,
|
|
recovery_hcd->image_info.app_firmware.size,
|
|
recovery_hcd->image_info.app_firmware.data,
|
|
recovery_hcd->image_info.app_firmware.size,
|
|
recovery_hcd->image_info.app_firmware.size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy payload\n");
|
|
goto do_program_exit;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"image_size = %d\n",
|
|
image_size);
|
|
|
|
retval = recovery_flash_romboot_erase(tcm_hcd);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to erase chip");
|
|
goto do_program_exit;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Do recovery_flash_romboot_reprogram");
|
|
retval = recovery_flash_romboot_reprogram(tcm_hcd, out_buf, image_size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to program chip");
|
|
goto do_program_exit;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Do reset and re-init");
|
|
retval = tcm_hcd->reset_n_reinit(tcm_hcd, false, true);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to reset");
|
|
}
|
|
|
|
do_program_exit:
|
|
|
|
atomic_set(&tcm_hcd->firmware_flashing, 0);
|
|
|
|
pm_relax(&tcm_hcd->pdev->dev);
|
|
|
|
mutex_unlock(&tcm_hcd->extif_mutex);
|
|
|
|
kfree(out_buf);
|
|
|
|
return;
|
|
}
|
|
|
|
static ssize_t recovery_sysfs_f35_recovery_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int retval;
|
|
unsigned int input;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
if (sscanf(buf, "%u", &input) != 1)
|
|
return -EINVAL;
|
|
|
|
if (input == 1)
|
|
recovery_hcd->set_up_recovery_mode = true;
|
|
else if (input == 2)
|
|
recovery_hcd->set_up_recovery_mode = false;
|
|
else
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&tcm_hcd->extif_mutex);
|
|
|
|
if (recovery_hcd->ihex_size == 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get ihex data\n");
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (recovery_hcd->ihex_size % IHEX_RECORD_SIZE) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Invalid ihex data\n");
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE;
|
|
|
|
retval = recovery_do_f35_recovery();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to do recovery\n");
|
|
goto exit;
|
|
}
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE;
|
|
|
|
mutex_unlock(&tcm_hcd->extif_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t recovery_sysfs_ihex_store(struct file *data_file,
|
|
struct kobject *kobj, struct bin_attribute *attributes,
|
|
char *buf, loff_t pos, size_t count)
|
|
{
|
|
int retval;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
mutex_lock(&tcm_hcd->extif_mutex);
|
|
|
|
retval = secure_memcpy(&recovery_hcd->ihex_buf[pos],
|
|
IHEX_BUF_SIZE - pos,
|
|
buf,
|
|
count,
|
|
count);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy ihex data\n");
|
|
recovery_hcd->ihex_size = 0;
|
|
goto exit;
|
|
}
|
|
|
|
recovery_hcd->ihex_size = pos + count;
|
|
|
|
retval = count;
|
|
|
|
exit:
|
|
mutex_unlock(&tcm_hcd->extif_mutex);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_device_reset(void)
|
|
{
|
|
int retval;
|
|
unsigned char command;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
|
|
|
command = F35_RESET_COMMAND;
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
recovery_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");
|
|
return retval;
|
|
}
|
|
|
|
msleep(bdata->reset_delay_ms);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_add_data_entry(unsigned char data)
|
|
{
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
if (recovery_hcd->data_entries >= DATA_BUF_SIZE) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Reached data buffer size limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
recovery_hcd->data_buf[recovery_hcd->data_entries++] = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_add_romboot_data_entry(unsigned char data)
|
|
{
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
if (recovery_hcd->data_entries >= DATA_ROMBOOT_BUF_SIZE) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Reached data buffer size limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
recovery_hcd->data_buf[recovery_hcd->data_entries++] = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_add_padding(unsigned int *words)
|
|
{
|
|
int retval;
|
|
unsigned int padding;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
padding = (F35_CHUNK_SIZE_WORDS - *words % F35_CHUNK_SIZE_WORDS);
|
|
padding %= F35_CHUNK_SIZE_WORDS;
|
|
|
|
while (padding) {
|
|
retval = recovery_add_data_entry(0xff);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_add_data_entry(0xff);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
|
|
(*words)++;
|
|
padding--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_parse_f35_ihex(void)
|
|
{
|
|
int retval;
|
|
unsigned char colon;
|
|
unsigned char *buf;
|
|
unsigned int addr;
|
|
unsigned int type;
|
|
unsigned int addrl;
|
|
unsigned int addrh;
|
|
unsigned int data0;
|
|
unsigned int data1;
|
|
unsigned int count;
|
|
unsigned int words;
|
|
unsigned int offset;
|
|
unsigned int record;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
words = 0;
|
|
|
|
offset = 0;
|
|
|
|
buf = recovery_hcd->ihex_buf;
|
|
|
|
recovery_hcd->data_entries = 0;
|
|
|
|
for (record = 0; record < recovery_hcd->ihex_records; record++) {
|
|
buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00;
|
|
retval = sscanf(&buf[record * IHEX_RECORD_SIZE],
|
|
"%c%02x%02x%02x%02x%02x%02x",
|
|
&colon,
|
|
&count,
|
|
&addrh,
|
|
&addrl,
|
|
&type,
|
|
&data0,
|
|
&data1);
|
|
if (retval != 7) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read ihex record\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (type == 0x00) {
|
|
if ((words % F35_CHUNK_SIZE_WORDS) == 0) {
|
|
addr = (addrh << 8) + addrl;
|
|
addr += offset;
|
|
addr >>= 4;
|
|
|
|
retval = recovery_add_data_entry(addr);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_add_data_entry(addr >> 8);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
retval = recovery_add_data_entry(data0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = recovery_add_data_entry(data1);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add data entry\n");
|
|
return retval;
|
|
}
|
|
|
|
words++;
|
|
} else if (type == 0x02) {
|
|
retval = recovery_add_padding(&words);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add padding\n");
|
|
return retval;
|
|
}
|
|
|
|
offset = (data0 << 8) + data1;
|
|
offset <<= 4;
|
|
}
|
|
}
|
|
|
|
retval = recovery_add_padding(&words);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to add padding\n");
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_check_status(void)
|
|
{
|
|
int retval;
|
|
unsigned char status;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
recovery_hcd->f35_addr.data_base,
|
|
&status,
|
|
sizeof(status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read status\n");
|
|
return retval;
|
|
}
|
|
|
|
status = status & 0x1f;
|
|
|
|
if (status != 0x00) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Recovery mode status = 0x%02x\n",
|
|
status);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_write_flash(void)
|
|
{
|
|
int retval;
|
|
unsigned char *data_ptr;
|
|
unsigned int chunk_buf_size;
|
|
unsigned int chunk_data_size;
|
|
unsigned int entries_written;
|
|
unsigned int entries_to_write;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
entries_written = 0;
|
|
|
|
data_ptr = recovery_hcd->data_buf;
|
|
|
|
chunk_buf_size = sizeof(recovery_hcd->chunk_buf);
|
|
|
|
chunk_data_size = chunk_buf_size - 1;
|
|
|
|
recovery_hcd->chunk_buf[chunk_buf_size - 1] = F35_WRITE_CHUNK_COMMAND;
|
|
|
|
while (entries_written < recovery_hcd->data_entries) {
|
|
entries_to_write = F35_CHUNK_SIZE + 2;
|
|
|
|
retval = secure_memcpy(recovery_hcd->chunk_buf,
|
|
chunk_buf_size - 1,
|
|
data_ptr,
|
|
recovery_hcd->data_entries - entries_written,
|
|
entries_to_write);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to copy chunk data\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
recovery_hcd->f35_addr.control_base,
|
|
recovery_hcd->chunk_buf,
|
|
chunk_buf_size);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write chunk data\n");
|
|
return retval;
|
|
}
|
|
|
|
data_ptr += entries_to_write;
|
|
entries_written += entries_to_write;
|
|
}
|
|
|
|
retval = recovery_check_status();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get no error recovery mode status\n");
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_poll_erase_completion(void)
|
|
{
|
|
int retval;
|
|
unsigned char status;
|
|
unsigned char command;
|
|
unsigned char data_base;
|
|
unsigned int timeout;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
timeout = F35_ERASE_ALL_WAIT_MS;
|
|
|
|
data_base = recovery_hcd->f35_addr.data_base;
|
|
|
|
do {
|
|
command = F35_READ_FLASH_STATUS_COMMAND;
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
recovery_hcd->f35_addr.command_base,
|
|
&command,
|
|
sizeof(command));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write F$35 command\n");
|
|
return retval;
|
|
}
|
|
|
|
do {
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
recovery_hcd->f35_addr.command_base,
|
|
&command,
|
|
sizeof(command));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read command status\n");
|
|
return retval;
|
|
}
|
|
|
|
if (command == 0x00)
|
|
break;
|
|
|
|
if (timeout == 0)
|
|
break;
|
|
|
|
msleep(F35_ERASE_ALL_POLL_MS);
|
|
timeout -= F35_ERASE_ALL_POLL_MS;
|
|
} while (true);
|
|
|
|
if (command != 0 && timeout == 0) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
retval = ovt_tcm_rmi_read(tcm_hcd,
|
|
data_base + F35_DATA5_OFFSET,
|
|
&status,
|
|
sizeof(status));
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to read flash status\n");
|
|
return retval;
|
|
}
|
|
|
|
if ((status & 0x01) == 0x00)
|
|
break;
|
|
|
|
if (timeout == 0) {
|
|
retval = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
msleep(F35_ERASE_ALL_POLL_MS);
|
|
timeout -= F35_ERASE_ALL_POLL_MS;
|
|
} while (true);
|
|
|
|
retval = 0;
|
|
|
|
exit:
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get erase completion\n");
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_erase_flash(void)
|
|
{
|
|
int retval;
|
|
unsigned char command;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
command = F35_ERASE_ALL_COMMAND;
|
|
|
|
retval = ovt_tcm_rmi_write(tcm_hcd,
|
|
recovery_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");
|
|
return retval;
|
|
}
|
|
|
|
if (recovery_hcd->f35_addr.command_base) {
|
|
retval = recovery_poll_erase_completion();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to wait for erase completion\n");
|
|
return retval;
|
|
}
|
|
} else {
|
|
msleep(F35_ERASE_ALL_WAIT_MS);
|
|
}
|
|
|
|
retval = recovery_check_status();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to get no error recovery mode status\n");
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_set_up_recovery_mode(void)
|
|
{
|
|
int retval;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
|
|
|
retval = tcm_hcd->identify(tcm_hcd, true);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to do identification\n");
|
|
return retval;
|
|
}
|
|
|
|
if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) {
|
|
retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to enter bootloader mode\n");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
retval = tcm_hcd->write_message(tcm_hcd,
|
|
recovery_hcd->out_buf[0],
|
|
&recovery_hcd->out_buf[1],
|
|
2,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write command %s\n",
|
|
STR(CMD_REBOOT_TO_ROM_BOOTLOADER));
|
|
return retval;
|
|
}
|
|
|
|
msleep(bdata->reset_delay_ms);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_do_f35_recovery(void)
|
|
{
|
|
int retval;
|
|
struct rmi_pdt_entry p_entry;
|
|
struct ovt_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
|
|
|
retval = recovery_parse_f35_ihex();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to parse ihex data\n");
|
|
return retval;
|
|
}
|
|
|
|
if (recovery_hcd->set_up_recovery_mode) {
|
|
retval = recovery_set_up_recovery_mode();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to set up recovery mode\n");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
#ifdef WATCHDOG_SW
|
|
tcm_hcd->update_watchdog(tcm_hcd, false);
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
if (p_entry.fn_number != UBL_FN_NUMBER) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to find F$35\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
recovery_hcd->f35_addr.query_base = p_entry.query_base_addr;
|
|
recovery_hcd->f35_addr.command_base = p_entry.command_base_addr;
|
|
recovery_hcd->f35_addr.control_base = p_entry.control_base_addr;
|
|
recovery_hcd->f35_addr.data_base = p_entry.data_base_addr;
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Start of recovery\n");
|
|
|
|
retval = recovery_erase_flash();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to erase flash\n");
|
|
return retval;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Flash erased\n");
|
|
|
|
retval = recovery_write_flash();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to write to flash\n");
|
|
return retval;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"Flash written\n");
|
|
|
|
retval = recovery_device_reset();
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to do reset\n");
|
|
return retval;
|
|
}
|
|
|
|
LOGN(tcm_hcd->pdev->dev.parent,
|
|
"End of recovery\n");
|
|
|
|
if (recovery_hcd->set_up_recovery_mode)
|
|
return 0;
|
|
|
|
#ifdef WATCHDOG_SW
|
|
tcm_hcd->update_watchdog(tcm_hcd, true);
|
|
#endif
|
|
|
|
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to enable interrupt\n");
|
|
return retval;
|
|
}
|
|
|
|
retval = tcm_hcd->identify(tcm_hcd, true);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to do identification\n");
|
|
return retval;
|
|
}
|
|
|
|
if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) {
|
|
retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to run application firmware\n");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_init(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
int idx;
|
|
|
|
if (tcm_hcd->in_hdl_mode)
|
|
return 0;
|
|
|
|
recovery_hcd = kzalloc(sizeof(*recovery_hcd), GFP_KERNEL);
|
|
if (!recovery_hcd) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for recovery_hcd\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
recovery_hcd->ihex_buf = kzalloc(IHEX_BUF_SIZE, GFP_KERNEL);
|
|
if (!recovery_hcd->ihex_buf) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for recovery_hcd->ihex_buf\n");
|
|
retval = -ENOMEM;
|
|
goto err_allocate_ihex_buf;
|
|
}
|
|
|
|
recovery_hcd->data_buf = kzalloc(DATA_BUF_SIZE, GFP_KERNEL);
|
|
if (!recovery_hcd->data_buf) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to allocate memory for recovery_hcd->data_buf\n");
|
|
retval = -ENOMEM;
|
|
goto err_allocate_data_buf;
|
|
}
|
|
|
|
recovery_hcd->tcm_hcd = tcm_hcd;
|
|
|
|
recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE;
|
|
|
|
recovery_hcd->out_buf[0] = CMD_REBOOT_TO_ROM_BOOTLOADER;
|
|
recovery_hcd->out_buf[1] = 0;
|
|
recovery_hcd->out_buf[2] = 0;
|
|
|
|
if (ENABLE_SYSFS_RECOVERY == false)
|
|
goto init_finished;
|
|
|
|
recovery_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME,
|
|
tcm_hcd->sysfs_dir);
|
|
if (!recovery_hcd->sysfs_dir) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to create sysfs directory\n");
|
|
retval = -EINVAL;
|
|
goto err_sysfs_create_dir;
|
|
}
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
|
//retval = sysfs_create_file(recovery_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");
|
|
goto err_sysfs_create_file;
|
|
}
|
|
}
|
|
|
|
retval = sysfs_create_bin_file(recovery_hcd->sysfs_dir, &bin_attr);
|
|
if (retval < 0) {
|
|
LOGE(tcm_hcd->pdev->dev.parent,
|
|
"Failed to create sysfs bin file\n");
|
|
goto err_sysfs_create_bin_file;
|
|
}
|
|
|
|
init_finished:
|
|
return 0;
|
|
|
|
err_sysfs_create_bin_file:
|
|
err_sysfs_create_file:
|
|
for (idx--; idx >= 0; idx--)
|
|
sysfs_remove_file(recovery_hcd->sysfs_dir, &(*attrs[idx]).attr);
|
|
|
|
kobject_put(recovery_hcd->sysfs_dir);
|
|
|
|
err_sysfs_create_dir:
|
|
kfree(recovery_hcd->data_buf);
|
|
err_allocate_data_buf:
|
|
kfree(recovery_hcd->ihex_buf);
|
|
err_allocate_ihex_buf:
|
|
kfree(recovery_hcd);
|
|
recovery_hcd = NULL;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int recovery_remove(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int idx;
|
|
|
|
if (!recovery_hcd)
|
|
goto exit;
|
|
|
|
if (ENABLE_SYSFS_RECOVERY == true) {
|
|
sysfs_remove_bin_file(recovery_hcd->sysfs_dir, &bin_attr);
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
|
sysfs_remove_file(recovery_hcd->sysfs_dir,
|
|
&(*attrs[idx]).attr);
|
|
}
|
|
|
|
kobject_put(recovery_hcd->sysfs_dir);
|
|
}
|
|
|
|
kfree(recovery_hcd->data_buf);
|
|
kfree(recovery_hcd->ihex_buf);
|
|
kfree(recovery_hcd);
|
|
recovery_hcd = NULL;
|
|
|
|
exit:
|
|
complete(&recovery_remove_complete);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int recovery_reinit(struct ovt_tcm_hcd *tcm_hcd)
|
|
{
|
|
int retval;
|
|
|
|
if (!recovery_hcd) {
|
|
retval = recovery_init(tcm_hcd);
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ovt_tcm_module_cb recovery_module = {
|
|
.type = TCM_RECOVERY,
|
|
.init = recovery_init,
|
|
.remove = recovery_remove,
|
|
.syncbox = NULL,
|
|
#ifdef REPORT_NOTIFIER
|
|
.asyncbox = NULL,
|
|
#endif
|
|
.reinit = recovery_reinit,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
.early_suspend = NULL,
|
|
};
|
|
|
|
#ifdef BUILD_AS_KO_MODULE
|
|
|
|
int recovery_module_init(void)
|
|
{
|
|
return ovt_tcm_add_module(&recovery_module, true);
|
|
}
|
|
|
|
void recovery_module_exit(void)
|
|
{
|
|
ovt_tcm_add_module(&recovery_module, false);
|
|
|
|
wait_for_completion(&recovery_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
EXPORT_SYMBOL(recovery_module_init);
|
|
EXPORT_SYMBOL(recovery_module_exit);
|
|
|
|
#else
|
|
|
|
static int __init recovery_module_init(void)
|
|
{
|
|
return ovt_tcm_add_module(&recovery_module, true);
|
|
}
|
|
|
|
static void __exit recovery_module_exit(void)
|
|
{
|
|
ovt_tcm_add_module(&recovery_module, false);
|
|
|
|
wait_for_completion(&recovery_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
module_init(recovery_module_init);
|
|
module_exit(recovery_module_exit);
|
|
|
|
MODULE_AUTHOR("omnivision, Inc.");
|
|
MODULE_DESCRIPTION("omnivision TCM Recovery Module");
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
#endif
|