1
0
Fork 0
alistair23-linux/drivers/input/touchscreen/cyttsp5_loader.c

1571 lines
40 KiB
C

/*
* cyttsp5_loader.c
* Parade TrueTouch(TM) Standard Product V5 FW Loader Module.
* For use with Parade touchscreen controllers.
* Supported parts include:
* CYTMA5XX
* CYTMA448
* CYTMA445A
* CYTT21XXX
* CYTT31XXX
*
* Copyright (C) 2015 Parade Technologies
* Copyright (C) 2012-2015 Cypress Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, and only version 2, as published by the
* Free Software Foundation.
*
* 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.
*
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
*
*/
#include "cyttsp5_regs.h"
#include <linux/firmware.h>
#define CYTTSP5_LOADER_NAME "cyttsp5_loader"
#define CY_FW_MANUAL_UPGRADE_FILE_NAME "cyttsp5_fw_manual_upgrade"
/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition
* to perform FW and config upgrade during probe
* instead of scheduling a work for it
*/
/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */
#define CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW 1
#define CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT 3
#define CYTTSP5_FW_UPGRADE \
(defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE) \
|| defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE))
#define CYTTSP5_TTCONFIG_UPGRADE \
(defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE) \
|| defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE))
static const u8 cyttsp5_security_key[] = {
0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A
};
/* Timeout values in ms. */
#define CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT 500
#define CY_LDR_SWITCH_TO_APP_MODE_TIMEOUT 300
#define CY_MAX_STATUS_SIZE 32
#define CY_DATA_MAX_ROW_SIZE 256
#define CY_DATA_ROW_SIZE 128
#define CY_ARRAY_ID_OFFSET 0
#define CY_ROW_NUM_OFFSET 1
#define CY_ROW_SIZE_OFFSET 3
#define CY_ROW_DATA_OFFSET 5
#define CY_POST_TT_CFG_CRC_MASK 0x2
struct cyttsp5_loader_data {
struct device *dev;
struct cyttsp5_sysinfo *si;
u8 status_buf[CY_MAX_STATUS_SIZE];
struct completion int_running;
struct completion calibration_complete;
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
struct completion builtin_bin_fw_complete;
int builtin_bin_fw_status;
bool is_manual_upgrade_enabled;
#endif
struct work_struct fw_and_config_upgrade;
struct work_struct calibration_work;
struct cyttsp5_loader_platform_data *loader_pdata;
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
struct mutex config_lock;
u8 *config_data;
int config_size;
bool config_loading;
#endif
};
struct cyttsp5_dev_id {
u32 silicon_id;
u8 rev_id;
u32 bl_ver;
};
struct cyttsp5_hex_image {
u8 array_id;
u16 row_num;
u16 row_size;
u8 row_data[CY_DATA_ROW_SIZE];
} __packed;
static struct cyttsp5_core_commands *cmd;
static struct cyttsp5_module loader_module;
static inline struct cyttsp5_loader_data *cyttsp5_get_loader_data(
struct device *dev)
{
return cyttsp5_get_module_data(dev, &loader_module);
}
#if CYTTSP5_FW_UPGRADE \
|| defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE)
static u8 cyttsp5_get_panel_id(struct device *dev)
{
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
return cd->panel_id;
}
#endif
#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE
/*
* return code:
* -1: Do not upgrade firmware
* 0: Version info same, let caller decide
* 1: Do a firmware upgrade
*/
static int cyttsp5_check_firmware_version(struct device *dev,
u32 fw_ver_new, u32 fw_revctrl_new)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u32 fw_ver_img;
u32 fw_revctrl_img;
fw_ver_img = ld->si->cydata.fw_ver_major << 8;
fw_ver_img += ld->si->cydata.fw_ver_minor;
dev_dbg(dev, "%s: img vers:0x%04X new vers:0x%04X\n", __func__,
fw_ver_img, fw_ver_new);
if (fw_ver_new > fw_ver_img) {
dev_dbg(dev, "%s: Image is newer, will upgrade\n",
__func__);
return 1;
}
if (fw_ver_new < fw_ver_img) {
dev_dbg(dev, "%s: Image is older, will NOT upgrade\n",
__func__);
return -1;
}
fw_revctrl_img = ld->si->cydata.revctrl;
dev_dbg(dev, "%s: img revctrl:0x%04X new revctrl:0x%04X\n",
__func__, fw_revctrl_img, fw_revctrl_new);
if (fw_revctrl_new > fw_revctrl_img) {
dev_dbg(dev, "%s: Image is newer, will upgrade\n",
__func__);
return 1;
}
if (fw_revctrl_new < fw_revctrl_img) {
dev_dbg(dev, "%s: Image is older, will NOT upgrade\n",
__func__);
return -1;
}
return 0;
}
static void cyttsp5_calibrate_idacs(struct work_struct *calibration_work)
{
struct cyttsp5_loader_data *ld = container_of(calibration_work,
struct cyttsp5_loader_data, calibration_work);
struct device *dev = ld->dev;
u8 mode;
u8 status;
int rc;
rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0)
goto exit;
rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
if (rc < 0)
goto release;
for (mode = 0; mode < 3; mode++) {
rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode, &status);
if (rc < 0)
goto release;
}
rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
if (rc < 0)
goto release;
dev_dbg(dev, "%s: Calibration Done\n", __func__);
release:
cmd->release_exclusive(dev);
exit:
complete(&ld->calibration_complete);
}
static int cyttsp5_calibration_attention(struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
int rc = 0;
schedule_work(&ld->calibration_work);
cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_LOADER_NAME,
cyttsp5_calibration_attention, 0);
return rc;
}
#endif /* CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE */
#if CYTTSP5_FW_UPGRADE
static u8 *cyttsp5_get_row_(struct device *dev, u8 *row_buf,
u8 *image_buf, int size)
{
memcpy(row_buf, image_buf, size);
return image_buf + size;
}
static int cyttsp5_ldr_enter_(struct device *dev, struct cyttsp5_dev_id *dev_id)
{
int rc;
u8 return_data[8];
u8 mode;
dev_id->silicon_id = 0;
dev_id->rev_id = 0;
dev_id->bl_ver = 0;
cmd->request_reset(dev);
rc = cmd->request_get_mode(dev, 0, &mode);
if (rc < 0)
return rc;
if (mode == CY_MODE_UNKNOWN)
return -EINVAL;
if (mode == CY_MODE_OPERATIONAL) {
rc = cmd->nonhid_cmd->start_bl(dev, 0);
if (rc < 0)
return rc;
}
rc = cmd->nonhid_cmd->get_bl_info(dev, 0, return_data);
if (rc < 0)
return rc;
dev_id->silicon_id = get_unaligned_le32(&return_data[0]);
dev_id->rev_id = return_data[4];
dev_id->bl_ver = return_data[5] + (return_data[6] << 8)
+ (return_data[7] << 16);
return 0;
}
static int cyttsp5_ldr_init_(struct device *dev,
struct cyttsp5_hex_image *row_image)
{
return cmd->nonhid_cmd->initiate_bl(dev, 0, 8,
(u8 *)cyttsp5_security_key, row_image->row_size,
row_image->row_data);
}
static int cyttsp5_ldr_parse_row_(struct device *dev, u8 *row_buf,
struct cyttsp5_hex_image *row_image)
{
int rc = 0;
row_image->array_id = row_buf[CY_ARRAY_ID_OFFSET];
row_image->row_num = get_unaligned_be16(&row_buf[CY_ROW_NUM_OFFSET]);
row_image->row_size = get_unaligned_be16(&row_buf[CY_ROW_SIZE_OFFSET]);
if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) {
dev_err(dev, "%s: row data buffer overflow\n", __func__);
rc = -EOVERFLOW;
goto cyttsp5_ldr_parse_row_exit;
}
memcpy(row_image->row_data, &row_buf[CY_ROW_DATA_OFFSET],
row_image->row_size);
cyttsp5_ldr_parse_row_exit:
return rc;
}
static int cyttsp5_ldr_prog_row_(struct device *dev,
struct cyttsp5_hex_image *row_image)
{
u16 length = row_image->row_size + 3;
u8 data[3 + row_image->row_size];
u8 offset = 0;
data[offset++] = row_image->array_id;
data[offset++] = LOW_BYTE(row_image->row_num);
data[offset++] = HI_BYTE(row_image->row_num);
memcpy(data + 3, row_image->row_data, row_image->row_size);
return cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data);
}
static int cyttsp5_ldr_verify_chksum_(struct device *dev)
{
u8 result;
int rc;
rc = cmd->nonhid_cmd->verify_app_integrity(dev, 0, &result);
if (rc)
return rc;
/* fail */
if (result == 0)
return -EINVAL;
return 0;
}
static int cyttsp5_ldr_exit_(struct device *dev)
{
return cmd->nonhid_cmd->launch_app(dev, 0);
}
static int cyttsp5_load_app_(struct device *dev, const u8 *fw, int fw_size)
{
struct cyttsp5_dev_id *dev_id;
struct cyttsp5_hex_image *row_image;
u8 *row_buf;
size_t image_rec_size;
size_t row_buf_size = CY_DATA_MAX_ROW_SIZE;
int row_count = 0;
u8 *p;
u8 *last_row;
int rc;
int rc_tmp;
image_rec_size = sizeof(struct cyttsp5_hex_image);
if (fw_size % image_rec_size != 0) {
dev_err(dev, "%s: Firmware image is misaligned\n", __func__);
rc = -EINVAL;
goto _cyttsp5_load_app_error;
}
dev_info(dev, "%s: start load app\n", __func__);
#ifdef TTHE_TUNER_SUPPORT
cmd->request_tthe_print(dev, NULL, 0, "start load app");
#endif
row_buf = kzalloc(row_buf_size, GFP_KERNEL);
row_image = kzalloc(sizeof(struct cyttsp5_hex_image), GFP_KERNEL);
dev_id = kzalloc(sizeof(struct cyttsp5_dev_id), GFP_KERNEL);
if (!row_buf || !row_image || !dev_id) {
rc = -ENOMEM;
goto _cyttsp5_load_app_exit;
}
cmd->request_stop_wd(dev);
dev_info(dev, "%s: Send BL Loader Enter\n", __func__);
#ifdef TTHE_TUNER_SUPPORT
cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter");
#endif
rc = cyttsp5_ldr_enter_(dev, dev_id);
if (rc) {
dev_err(dev, "%s: Error cannot start Loader (ret=%d)\n",
__func__, rc);
goto _cyttsp5_load_app_exit;
}
dev_vdbg(dev, "%s: dev: silicon id=%08X rev=%02X bl=%08X\n",
__func__, dev_id->silicon_id,
dev_id->rev_id, dev_id->bl_ver);
/* get last row */
last_row = (u8 *)fw + fw_size - image_rec_size;
cyttsp5_get_row_(dev, row_buf, last_row, image_rec_size);
cyttsp5_ldr_parse_row_(dev, row_buf, row_image);
/* initialise bootloader */
rc = cyttsp5_ldr_init_(dev, row_image);
if (rc) {
dev_err(dev, "%s: Error cannot init Loader (ret=%d)\n",
__func__, rc);
goto _cyttsp5_load_app_exit;
}
dev_info(dev, "%s: Send BL Loader Blocks\n", __func__);
#ifdef TTHE_TUNER_SUPPORT
cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Blocks");
#endif
p = (u8 *)fw;
while (p < last_row) {
/* Get row */
dev_dbg(dev, "%s: read row=%d\n", __func__, ++row_count);
memset(row_buf, 0, row_buf_size);
p = cyttsp5_get_row_(dev, row_buf, p, image_rec_size);
/* Parse row */
dev_vdbg(dev, "%s: p=%p buf=%p buf[0]=%02X\n",
__func__, p, row_buf, row_buf[0]);
rc = cyttsp5_ldr_parse_row_(dev, row_buf, row_image);
dev_vdbg(dev, "%s: array_id=%02X row_num=%04X(%d) row_size=%04X(%d)\n",
__func__, row_image->array_id,
row_image->row_num, row_image->row_num,
row_image->row_size, row_image->row_size);
if (rc) {
dev_err(dev, "%s: Parse Row Error (a=%d r=%d ret=%d\n",
__func__, row_image->array_id,
row_image->row_num, rc);
goto _cyttsp5_load_app_exit;
} else {
dev_vdbg(dev, "%s: Parse Row (a=%d r=%d ret=%d\n",
__func__, row_image->array_id,
row_image->row_num, rc);
}
/* program row */
rc = cyttsp5_ldr_prog_row_(dev, row_image);
if (rc) {
dev_err(dev, "%s: Program Row Error (array=%d row=%d ret=%d)\n",
__func__, row_image->array_id,
row_image->row_num, rc);
goto _cyttsp5_load_app_exit;
}
dev_vdbg(dev, "%s: array=%d row_cnt=%d row_num=%04X\n",
__func__, row_image->array_id, row_count,
row_image->row_num);
}
/* exit loader */
dev_info(dev, "%s: Send BL Loader Terminate\n", __func__);
#ifdef TTHE_TUNER_SUPPORT
cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate");
#endif
rc = cyttsp5_ldr_exit_(dev);
if (rc) {
dev_err(dev, "%s: Error on exit Loader (ret=%d)\n",
__func__, rc);
/* verify app checksum */
rc_tmp = cyttsp5_ldr_verify_chksum_(dev);
if (rc_tmp)
dev_err(dev, "%s: ldr_verify_chksum fail r=%d\n",
__func__, rc_tmp);
else
dev_info(dev, "%s: APP Checksum Verified\n", __func__);
}
_cyttsp5_load_app_exit:
kfree(row_buf);
kfree(row_image);
kfree(dev_id);
_cyttsp5_load_app_error:
return rc;
}
static int cyttsp5_upgrade_firmware(struct device *dev, const u8 *fw_img,
int fw_size)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
int retry = CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT;
bool wait_for_calibration_complete = false;
int rc;
pm_runtime_get_sync(dev);
rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0)
goto exit;
while (retry--) {
rc = cyttsp5_load_app_(dev, fw_img, fw_size);
if (rc < 0)
dev_err(dev, "%s: Firmware update failed rc=%d, retry:%d\n",
__func__, rc, retry);
else
break;
msleep(20);
}
if (rc < 0) {
dev_err(dev, "%s: Firmware update failed with error code %d\n",
__func__, rc);
} else if (ld->loader_pdata &&
(ld->loader_pdata->flags
& CY_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
reinit_completion(&ld->calibration_complete);
#else
INIT_COMPLETION(ld->calibration_complete);
#endif
/* set up call back for startup */
dev_vdbg(dev, "%s: Adding callback for calibration\n",
__func__);
rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP,
CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0);
if (rc) {
dev_err(dev, "%s: Failed adding callback for calibration\n",
__func__);
dev_err(dev, "%s: No calibration will be performed\n",
__func__);
rc = 0;
} else
wait_for_calibration_complete = true;
}
cmd->release_exclusive(dev);
exit:
if (!rc)
cmd->request_restart(dev, true);
pm_runtime_put_sync(dev);
if (wait_for_calibration_complete)
wait_for_completion(&ld->calibration_complete);
return rc;
}
static int cyttsp5_loader_attention(struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
complete(&ld->int_running);
return 0;
}
#endif /* CYTTSP5_FW_UPGRADE */
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
static int cyttsp5_check_firmware_version_platform(struct device *dev,
struct cyttsp5_touch_firmware *fw)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u32 fw_ver_new;
u32 fw_revctrl_new;
int upgrade;
if (!ld->si) {
dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
__func__);
return CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW;
}
fw_ver_new = get_unaligned_be16(fw->ver + 2);
/* 4 middle bytes are not used */
fw_revctrl_new = get_unaligned_be32(fw->ver + 8);
upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new,
fw_revctrl_new);
if (upgrade > 0)
return 1;
return 0;
}
static struct cyttsp5_touch_firmware *cyttsp5_get_platform_firmware(
struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
struct cyttsp5_touch_firmware **fws;
struct cyttsp5_touch_firmware *fw;
u8 panel_id;
panel_id = cyttsp5_get_panel_id(dev);
if (panel_id == PANEL_ID_NOT_ENABLED) {
dev_dbg(dev, "%s: Panel ID not enabled, using legacy firmware\n",
__func__);
return ld->loader_pdata->fw;
}
fws = ld->loader_pdata->fws;
if (!fws) {
dev_err(dev, "%s: No firmwares provided\n", __func__);
return NULL;
}
/* Find FW according to the Panel ID */
while ((fw = *fws++)) {
if (fw->panel_id == panel_id) {
dev_dbg(dev, "%s: Found matching fw:%p with Panel ID: 0x%02X\n",
__func__, fw, fw->panel_id);
return fw;
}
dev_vdbg(dev, "%s: Found mismatching fw:%p with Panel ID: 0x%02X\n",
__func__, fw, fw->panel_id);
}
return NULL;
}
static int upgrade_firmware_from_platform(struct device *dev,
bool forced)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
struct cyttsp5_touch_firmware *fw;
int rc = -ENODEV;
int upgrade;
if (!ld->loader_pdata) {
dev_err(dev, "%s: No loader platform data\n", __func__);
return rc;
}
fw = cyttsp5_get_platform_firmware(dev);
if (!fw || !fw->img || !fw->size) {
dev_err(dev, "%s: No platform firmware\n", __func__);
return rc;
}
if (!fw->ver || !fw->vsize) {
dev_err(dev, "%s: No platform firmware version\n",
__func__);
return rc;
}
if (forced)
upgrade = forced;
else
upgrade = cyttsp5_check_firmware_version_platform(dev, fw);
if (upgrade)
return cyttsp5_upgrade_firmware(dev, fw->img, fw->size);
return rc;
}
#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE */
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
static void _cyttsp5_firmware_cont(const struct firmware *fw, void *context)
{
struct device *dev = context;
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u8 header_size = 0;
if (!fw)
goto cyttsp5_firmware_cont_exit;
if (!fw->data || !fw->size) {
dev_err(dev, "%s: No firmware received\n", __func__);
goto cyttsp5_firmware_cont_release_exit;
}
header_size = fw->data[0];
if (header_size >= (fw->size + 1)) {
dev_err(dev, "%s: Firmware format is invalid\n", __func__);
goto cyttsp5_firmware_cont_release_exit;
}
cyttsp5_upgrade_firmware(dev, &(fw->data[header_size + 1]),
fw->size - (header_size + 1));
cyttsp5_firmware_cont_release_exit:
release_firmware(fw);
cyttsp5_firmware_cont_exit:
ld->is_manual_upgrade_enabled = 0;
}
static int cyttsp5_check_firmware_version_builtin(struct device *dev,
const struct firmware *fw)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u32 fw_ver_new;
u32 fw_revctrl_new;
int upgrade;
if (!ld->si) {
dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
__func__);
return CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW;
}
fw_ver_new = get_unaligned_be16(fw->data + 3);
/* 4 middle bytes are not used */
fw_revctrl_new = get_unaligned_be32(fw->data + 9);
upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new,
fw_revctrl_new);
if (upgrade > 0)
return 1;
return 0;
}
static void _cyttsp5_firmware_cont_builtin(const struct firmware *fw,
void *context)
{
struct device *dev = context;
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
int upgrade;
if (!fw) {
dev_info(dev, "%s: No builtin firmware\n", __func__);
goto _cyttsp5_firmware_cont_builtin_exit;
}
if (!fw->data || !fw->size) {
dev_err(dev, "%s: Invalid builtin firmware\n", __func__);
goto _cyttsp5_firmware_cont_builtin_exit;
}
dev_dbg(dev, "%s: Found firmware\n", __func__);
upgrade = cyttsp5_check_firmware_version_builtin(dev, fw);
if (upgrade) {
_cyttsp5_firmware_cont(fw, dev);
ld->builtin_bin_fw_status = 0;
complete(&ld->builtin_bin_fw_complete);
return;
}
_cyttsp5_firmware_cont_builtin_exit:
release_firmware(fw);
ld->builtin_bin_fw_status = -EINVAL;
complete(&ld->builtin_bin_fw_complete);
}
static int upgrade_firmware_from_class(struct device *dev)
{
int retval;
dev_vdbg(dev, "%s: Enabling firmware class loader\n", __func__);
retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
CY_FW_MANUAL_UPGRADE_FILE_NAME, dev, GFP_KERNEL, dev,
_cyttsp5_firmware_cont);
if (retval < 0) {
dev_err(dev, "%s: Fail request firmware class file load\n",
__func__);
return retval;
}
return 0;
}
/*
* Generates binary FW filename as following:
* - Panel ID not enabled: cyttsp5_fw.bin
* - Panel ID enabled: cyttsp5_fw_pidXX.bin
*/
static char *generate_firmware_filename(struct device *dev)
{
char *filename;
u8 panel_id;
#define FILENAME_LEN_MAX 64
filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
if (!filename)
return NULL;
panel_id = cyttsp5_get_panel_id(dev);
if (panel_id == PANEL_ID_NOT_ENABLED)
snprintf(filename, FILENAME_LEN_MAX, "%s", CY_FW_FILE_NAME);
else
snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s",
CY_FW_FILE_PREFIX, panel_id, CY_FW_FILE_SUFFIX);
dev_dbg(dev, "%s: Filename: %s\n", __func__, filename);
return filename;
}
static int upgrade_firmware_from_builtin(struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
char *filename;
int retval;
dev_vdbg(dev, "%s: Enabling firmware class loader built-in\n",
__func__);
filename = generate_firmware_filename(dev);
if (!filename)
return -ENOMEM;
retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
filename, dev, GFP_KERNEL, dev,
_cyttsp5_firmware_cont_builtin);
if (retval < 0) {
dev_err(dev, "%s: Fail request firmware class file load\n",
__func__);
goto exit;
}
/* wait until FW binary upgrade finishes */
wait_for_completion(&ld->builtin_bin_fw_complete);
retval = ld->builtin_bin_fw_status;
exit:
kfree(filename);
return retval;
}
#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE */
#if CYTTSP5_TTCONFIG_UPGRADE
static int cyttsp5_write_config_row_(struct device *dev, u8 ebid,
u16 row_number, u16 row_size, u8 *data)
{
int rc;
u16 actual_write_len;
rc = cmd->nonhid_cmd->write_conf_block(dev, 0, row_number,
row_size, ebid, data, (u8 *)cyttsp5_security_key,
&actual_write_len);
if (rc) {
dev_err(dev, "%s: Fail Put EBID=%d row=%d cmd fail r=%d\n",
__func__, ebid, row_number, rc);
return rc;
}
if (actual_write_len != row_size) {
dev_err(dev, "%s: Fail Put EBID=%d row=%d wrong write size=%d\n",
__func__, ebid, row_number, actual_write_len);
rc = -EINVAL;
}
return rc;
}
static int cyttsp5_upgrade_ttconfig(struct device *dev,
const u8 *ttconfig_data, int ttconfig_size)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
bool wait_for_calibration_complete = false;
u8 ebid = CY_TCH_PARM_EBID;
u16 row_size = CY_DATA_ROW_SIZE;
u16 table_size;
u16 row_count;
u16 residue;
u8 *row_buf;
u8 verify_crc_status;
u16 calculated_crc;
u16 stored_crc;
int rc = 0;
int i;
table_size = ttconfig_size;
row_count = table_size / row_size;
row_buf = (u8 *)ttconfig_data;
dev_dbg(dev, "%s: size:%d row_size=%d row_count=%d\n",
__func__, table_size, row_size, row_count);
pm_runtime_get_sync(dev);
rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0)
goto exit;
rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
if (rc < 0)
goto release;
for (i = 0; i < row_count; i++) {
dev_dbg(dev, "%s: row=%d size=%d\n", __func__, i, row_size);
rc = cyttsp5_write_config_row_(dev, ebid, i, row_size,
row_buf);
if (rc) {
dev_err(dev, "%s: Fail put row=%d r=%d\n",
__func__, i, rc);
break;
}
row_buf += row_size;
}
if (!rc) {
residue = table_size % row_size;
dev_dbg(dev, "%s: row=%d size=%d\n", __func__, i, residue);
rc = cyttsp5_write_config_row_(dev, ebid, i, residue,
row_buf);
row_count++;
if (rc)
dev_err(dev, "%s: Fail put row=%d r=%d\n",
__func__, i, rc);
}
if (!rc)
dev_dbg(dev, "%s: TT_CFG updated: rows:%d bytes:%d\n",
__func__, row_count, table_size);
rc = cmd->nonhid_cmd->verify_config_block_crc(dev, 0, ebid,
&verify_crc_status, &calculated_crc, &stored_crc);
if (rc || verify_crc_status)
dev_err(dev, "%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
__func__, ebid, verify_crc_status,
calculated_crc, stored_crc);
else
dev_dbg(dev, "%s: CRC PASS, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
__func__, ebid, verify_crc_status,
calculated_crc, stored_crc);
rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
if (rc < 0)
goto release;
if (ld->loader_pdata &&
(ld->loader_pdata->flags
& CY_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE)) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
reinit_completion(&ld->calibration_complete);
#else
INIT_COMPLETION(ld->calibration_complete);
#endif
/* set up call back for startup */
dev_vdbg(dev, "%s: Adding callback for calibration\n",
__func__);
rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP,
CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0);
if (rc) {
dev_err(dev, "%s: Failed adding callback for calibration\n",
__func__);
dev_err(dev, "%s: No calibration will be performed\n",
__func__);
rc = 0;
} else
wait_for_calibration_complete = true;
}
release:
cmd->release_exclusive(dev);
exit:
if (!rc)
cmd->request_restart(dev, true);
pm_runtime_put_sync(dev);
if (wait_for_calibration_complete)
wait_for_completion(&ld->calibration_complete);
return rc;
}
#endif /* CYTTSP5_TTCONFIG_UPGRADE */
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
static int cyttsp5_get_ttconfig_crc(struct device *dev,
const u8 *ttconfig_data, int ttconfig_size, u16 *crc)
{
u16 crc_loc;
crc_loc = get_unaligned_le16(&ttconfig_data[2]);
if (ttconfig_size < crc_loc + 2)
return -EINVAL;
*crc = get_unaligned_le16(&ttconfig_data[crc_loc]);
return 0;
}
static int cyttsp5_get_ttconfig_version(struct device *dev,
const u8 *ttconfig_data, int ttconfig_size, u16 *version)
{
if (ttconfig_size < CY_TTCONFIG_VERSION_OFFSET
+ CY_TTCONFIG_VERSION_SIZE)
return -EINVAL;
*version = get_unaligned_le16(
&ttconfig_data[CY_TTCONFIG_VERSION_OFFSET]);
return 0;
}
static int cyttsp5_check_ttconfig_version(struct device *dev,
const u8 *ttconfig_data, int ttconfig_size)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u16 cfg_crc_new;
int rc;
if (!ld->si)
return 0;
/* Check for config version */
if (ld->loader_pdata->flags &
CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION) {
u16 cfg_ver_new;
rc = cyttsp5_get_ttconfig_version(dev, ttconfig_data,
ttconfig_size, &cfg_ver_new);
if (rc)
return 0;
dev_dbg(dev, "%s: img_ver:0x%04X new_ver:0x%04X\n",
__func__, ld->si->cydata.fw_ver_conf, cfg_ver_new);
/* Check if config version is newer */
if (cfg_ver_new > ld->si->cydata.fw_ver_conf) {
dev_dbg(dev, "%s: Config version newer, will upgrade\n",
__func__);
return 1;
}
dev_dbg(dev, "%s: Config version is identical or older, will NOT upgrade\n",
__func__);
/* Check for config CRC */
} else {
rc = cyttsp5_get_ttconfig_crc(dev, ttconfig_data,
ttconfig_size, &cfg_crc_new);
if (rc)
return 0;
dev_dbg(dev, "%s: img_crc:0x%04X new_crc:0x%04X\n",
__func__, ld->si->ttconfig.crc, cfg_crc_new);
if (cfg_crc_new != ld->si->ttconfig.crc) {
dev_dbg(dev, "%s: Config CRC different, will upgrade\n",
__func__);
return 1;
}
dev_dbg(dev, "%s: Config CRC equal, will NOT upgrade\n",
__func__);
}
return 0;
}
static int cyttsp5_check_ttconfig_version_platform(struct device *dev,
struct cyttsp5_touch_config *ttconfig)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
u32 fw_ver_config;
u32 fw_revctrl_config;
if (!ld->si) {
dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
__func__);
return 0;
}
fw_ver_config = get_unaligned_be16(ttconfig->fw_ver + 2);
/* 4 middle bytes are not used */
fw_revctrl_config = get_unaligned_be32(ttconfig->fw_ver + 8);
/* FW versions should match */
if (cyttsp5_check_firmware_version(dev, fw_ver_config,
fw_revctrl_config)) {
dev_err(dev, "%s: FW versions mismatch\n", __func__);
return 0;
}
/* Check PowerOn Self Test, TT_CFG CRC bit */
if ((ld->si->cydata.post_code & CY_POST_TT_CFG_CRC_MASK) == 0) {
dev_dbg(dev, "%s: POST, TT_CFG failed (%X), will upgrade\n",
__func__, ld->si->cydata.post_code);
return 1;
}
return cyttsp5_check_ttconfig_version(dev, ttconfig->param_regs->data,
ttconfig->param_regs->size);
}
static struct cyttsp5_touch_config *cyttsp5_get_platform_ttconfig(
struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
struct cyttsp5_touch_config **ttconfigs;
struct cyttsp5_touch_config *ttconfig;
u8 panel_id;
panel_id = cyttsp5_get_panel_id(dev);
if (panel_id == PANEL_ID_NOT_ENABLED) {
/* TODO: Make debug message */
dev_info(dev, "%s: Panel ID not enabled, using legacy ttconfig\n",
__func__);
return ld->loader_pdata->ttconfig;
}
ttconfigs = ld->loader_pdata->ttconfigs;
if (!ttconfigs)
return NULL;
/* Find TT config according to the Panel ID */
while ((ttconfig = *ttconfigs++)) {
if (ttconfig->panel_id == panel_id) {
/* TODO: Make debug message */
dev_info(dev, "%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n",
__func__, ttconfig, ttconfig->panel_id);
return ttconfig;
}
dev_vdbg(dev, "%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n",
__func__, ttconfig, ttconfig->panel_id);
}
return NULL;
}
static int upgrade_ttconfig_from_platform(struct device *dev)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
struct cyttsp5_touch_config *ttconfig;
struct touch_settings *param_regs;
struct cyttsp5_touch_fw;
int rc = -ENODEV;
int upgrade;
if (!ld->loader_pdata) {
dev_info(dev, "%s: No loader platform data\n", __func__);
return rc;
}
ttconfig = cyttsp5_get_platform_ttconfig(dev);
if (!ttconfig) {
dev_info(dev, "%s: No ttconfig data\n", __func__);
return rc;
}
param_regs = ttconfig->param_regs;
if (!param_regs) {
dev_info(dev, "%s: No touch parameters\n", __func__);
return rc;
}
if (!param_regs->data || !param_regs->size) {
dev_info(dev, "%s: Invalid touch parameters\n", __func__);
return rc;
}
if (!ttconfig->fw_ver || !ttconfig->fw_vsize) {
dev_info(dev, "%s: Invalid FW version for touch parameters\n",
__func__);
return rc;
}
upgrade = cyttsp5_check_ttconfig_version_platform(dev, ttconfig);
if (upgrade)
return cyttsp5_upgrade_ttconfig(dev, param_regs->data,
param_regs->size);
return rc;
}
#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE */
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
static ssize_t cyttsp5_config_data_write(struct file *filp,
struct kobject *kobj, struct bin_attribute *bin_attr,
char *buf, loff_t offset, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct cyttsp5_loader_data *data = cyttsp5_get_loader_data(dev);
u8 *p;
dev_vdbg(dev, "%s: offset:%lld count:%zu\n", __func__, offset, count);
mutex_lock(&data->config_lock);
if (!data->config_loading) {
mutex_unlock(&data->config_lock);
return -ENODEV;
}
p = krealloc(data->config_data, offset + count, GFP_KERNEL);
if (!p) {
kfree(data->config_data);
data->config_data = NULL;
mutex_unlock(&data->config_lock);
return -ENOMEM;
}
data->config_data = p;
memcpy(&data->config_data[offset], buf, count);
data->config_size += count;
mutex_unlock(&data->config_lock);
return count;
}
static struct bin_attribute bin_attr_config_data = {
.attr = {
.name = "config_data",
.mode = S_IWUSR,
},
.size = 0,
.write = cyttsp5_config_data_write,
};
static ssize_t cyttsp5_config_loading_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
bool config_loading;
mutex_lock(&ld->config_lock);
config_loading = ld->config_loading;
mutex_unlock(&ld->config_lock);
return sprintf(buf, "%d\n", config_loading);
}
static int cyttsp5_verify_ttconfig_binary(struct device *dev,
u8 *bin_config_data, int bin_config_size, u8 **start, int *len)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
int header_size;
u16 config_size;
u32 fw_ver_config;
u32 fw_revctrl_config;
if (!ld->si) {
dev_err(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
__func__);
return -ENODEV;
}
/*
* We need 11 bytes for FW version control info and at
* least 6 bytes in config (Length + Max Length + CRC)
*/
header_size = bin_config_data[0] + 1;
if (header_size < 11 || header_size >= bin_config_size - 6) {
dev_err(dev, "%s: Invalid header size %d\n", __func__,
header_size);
return -EINVAL;
}
fw_ver_config = get_unaligned_be16(&bin_config_data[1]);
/* 4 middle bytes are not used */
fw_revctrl_config = get_unaligned_be32(&bin_config_data[7]);
/* FW versions should match */
if (cyttsp5_check_firmware_version(dev, fw_ver_config,
fw_revctrl_config)) {
dev_err(dev, "%s: FW versions mismatch\n", __func__);
return -EINVAL;
}
config_size = get_unaligned_le16(&bin_config_data[header_size]);
/* Perform a simple size check (2 bytes for CRC) */
if (config_size != bin_config_size - header_size - 2) {
dev_err(dev, "%s: Config size invalid\n", __func__);
return -EINVAL;
}
*start = &bin_config_data[header_size];
*len = bin_config_size - header_size;
return 0;
}
/*
* 1: Start loading TT Config
* 0: End loading TT Config and perform upgrade
*-1: Exit loading
*/
static ssize_t cyttsp5_config_loading_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
long value;
u8 *start;
int length;
int rc;
rc = kstrtol(buf, 10, &value);
if (rc < 0 || value < -1 || value > 1) {
dev_err(dev, "%s: Invalid value\n", __func__);
return size;
}
mutex_lock(&ld->config_lock);
if (value == 1)
ld->config_loading = true;
else if (value == -1)
ld->config_loading = false;
else if (value == 0 && ld->config_loading) {
ld->config_loading = false;
if (ld->config_size == 0) {
dev_err(dev, "%s: No config data\n", __func__);
goto exit_free;
}
rc = cyttsp5_verify_ttconfig_binary(dev,
ld->config_data, ld->config_size,
&start, &length);
if (rc)
goto exit_free;
rc = cyttsp5_upgrade_ttconfig(dev, start, length);
}
exit_free:
kfree(ld->config_data);
ld->config_data = NULL;
ld->config_size = 0;
mutex_unlock(&ld->config_lock);
if (rc)
return rc;
return size;
}
static DEVICE_ATTR(config_loading, S_IRUSR | S_IWUSR,
cyttsp5_config_loading_show, cyttsp5_config_loading_store);
#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE */
static void cyttsp5_fw_and_config_upgrade(
struct work_struct *fw_and_config_upgrade)
{
struct cyttsp5_loader_data *ld = container_of(fw_and_config_upgrade,
struct cyttsp5_loader_data, fw_and_config_upgrade);
struct device *dev = ld->dev;
ld->si = cmd->request_sysinfo(dev);
if (!ld->si)
dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
__func__);
#if !CYTTSP5_FW_UPGRADE
dev_info(dev, "%s: No FW upgrade method selected!\n", __func__);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
if (!upgrade_firmware_from_platform(dev, false))
return;
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
if (!upgrade_firmware_from_builtin(dev))
return;
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
if (!upgrade_ttconfig_from_platform(dev))
return;
#endif
}
#if CYTTSP5_FW_UPGRADE
static int cyttsp5_fw_upgrade_cb(struct device *dev)
{
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
if (!upgrade_firmware_from_platform(dev, false))
return 1;
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
if (!upgrade_firmware_from_builtin(dev))
return 1;
#endif
return 0;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
static ssize_t cyttsp5_forced_upgrade_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int rc = upgrade_firmware_from_platform(dev, true);
if (rc)
return rc;
return size;
}
static DEVICE_ATTR(forced_upgrade, S_IWUSR,
NULL, cyttsp5_forced_upgrade_store);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
static ssize_t cyttsp5_manual_upgrade_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
int rc;
if (ld->is_manual_upgrade_enabled)
return -EBUSY;
ld->is_manual_upgrade_enabled = 1;
rc = upgrade_firmware_from_class(ld->dev);
if (rc < 0)
ld->is_manual_upgrade_enabled = 0;
return size;
}
static DEVICE_ATTR(manual_upgrade, S_IWUSR,
NULL, cyttsp5_manual_upgrade_store);
#endif
static int cyttsp5_loader_probe(struct device *dev, void **data)
{
struct cyttsp5_loader_data *ld;
struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
int rc;
if (!pdata || !pdata->loader_pdata) {
dev_err(dev, "%s: Missing platform data\n", __func__);
rc = -ENODEV;
goto error_no_pdata;
}
ld = kzalloc(sizeof(*ld), GFP_KERNEL);
if (!ld) {
rc = -ENOMEM;
goto error_alloc_data_failed;
}
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
rc = device_create_file(dev, &dev_attr_forced_upgrade);
if (rc) {
dev_err(dev, "%s: Error, could not create forced_upgrade\n",
__func__);
goto error_create_forced_upgrade;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
rc = device_create_file(dev, &dev_attr_manual_upgrade);
if (rc) {
dev_err(dev, "%s: Error, could not create manual_upgrade\n",
__func__);
goto error_create_manual_upgrade;
}
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
rc = device_create_file(dev, &dev_attr_config_loading);
if (rc) {
dev_err(dev, "%s: Error, could not create config_loading\n",
__func__);
goto error_create_config_loading;
}
rc = device_create_bin_file(dev, &bin_attr_config_data);
if (rc) {
dev_err(dev, "%s: Error, could not create config_data\n",
__func__);
goto error_create_config_data;
}
#endif
ld->loader_pdata = pdata->loader_pdata;
ld->dev = dev;
*data = ld;
#if CYTTSP5_FW_UPGRADE
init_completion(&ld->int_running);
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
init_completion(&ld->builtin_bin_fw_complete);
#endif
cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME,
cyttsp5_loader_attention, CY_MODE_BOOTLOADER);
cmd->subscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME,
cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN);
#endif
#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE
init_completion(&ld->calibration_complete);
INIT_WORK(&ld->calibration_work, cyttsp5_calibrate_idacs);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
mutex_init(&ld->config_lock);
#endif
#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE
/* Call FW and config upgrade directly in probe */
cyttsp5_fw_and_config_upgrade(&ld->fw_and_config_upgrade);
#else
INIT_WORK(&ld->fw_and_config_upgrade, cyttsp5_fw_and_config_upgrade);
schedule_work(&ld->fw_and_config_upgrade);
#endif
dev_info(dev, "%s: Successful probe %s\n", __func__, dev_name(dev));
return 0;
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
error_create_config_data:
device_remove_file(dev, &dev_attr_config_loading);
error_create_config_loading:
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
device_remove_file(dev, &dev_attr_manual_upgrade);
error_create_manual_upgrade:
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
device_remove_file(dev, &dev_attr_forced_upgrade);
error_create_forced_upgrade:
#endif
kfree(ld);
error_alloc_data_failed:
error_no_pdata:
dev_err(dev, "%s failed.\n", __func__);
return rc;
}
static void cyttsp5_loader_release(struct device *dev, void *data)
{
struct cyttsp5_loader_data *ld = (struct cyttsp5_loader_data *)data;
#if CYTTSP5_FW_UPGRADE
cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME,
cyttsp5_loader_attention, CY_MODE_BOOTLOADER);
cmd->unsubscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME,
cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
device_remove_bin_file(dev, &bin_attr_config_data);
device_remove_file(dev, &dev_attr_config_loading);
kfree(ld->config_data);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
device_remove_file(dev, &dev_attr_manual_upgrade);
#endif
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
device_remove_file(dev, &dev_attr_forced_upgrade);
#endif
kfree(ld);
}
static struct cyttsp5_module loader_module = {
.name = CYTTSP5_LOADER_NAME,
.probe = cyttsp5_loader_probe,
.release = cyttsp5_loader_release,
};
static int __init cyttsp5_loader_init(void)
{
int rc;
cmd = cyttsp5_get_commands();
if (!cmd)
return -EINVAL;
rc = cyttsp5_register_module(&loader_module);
if (rc < 0) {
pr_err("%s: Error, failed registering module\n",
__func__);
return rc;
}
pr_info("%s: Parade TTSP FW Loader Driver (Built %s) rc=%d\n",
__func__, CY_DRIVER_VERSION, rc);
return 0;
}
module_init(cyttsp5_loader_init);
static void __exit cyttsp5_loader_exit(void)
{
cyttsp5_unregister_module(&loader_module);
}
module_exit(cyttsp5_loader_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver");
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");