diff options
Diffstat (limited to 'drivers/sensorhub/ssp_firmware.c')
-rw-r--r-- | drivers/sensorhub/ssp_firmware.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/drivers/sensorhub/ssp_firmware.c b/drivers/sensorhub/ssp_firmware.c new file mode 100644 index 0000000..ae04287 --- /dev/null +++ b/drivers/sensorhub/ssp_firmware.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2012, Samsung Electronics Co. Ltd. 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. + * + */ +#include "ssp.h" + +#define SSP_FIRMWARE_REVISION 90100 + +/* Bootload mode cmd */ +#define BL_FW_NAME "ssp.fw" +#define BL_CRASHED_FW_NAME "ssp_crashed.fw" + +#define APP_SLAVE_ADDR 0x18 +#define BOOTLOADER_SLAVE_ADDR 0x26 + +/* Bootloader mode status */ +#define BL_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define BL_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define BL_FRAME_CRC_CHECK 0x02 +#define BL_FRAME_CRC_FAIL 0x03 +#define BL_FRAME_CRC_PASS 0x04 +#define BL_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define BL_BOOT_STATUS_MASK 0x3f + +/* Command to unlock bootloader */ +#define BL_UNLOCK_CMD_MSB 0xaa +#define BL_UNLOCK_CMD_LSB 0xdc + +unsigned int get_module_rev(void) +{ + return SSP_FIRMWARE_REVISION; +} + +static int check_bootloader(struct i2c_client *client, unsigned int uState) +{ + u8 uVal; + u8 uTemp; + +recheck: + if (i2c_master_recv(client, &uVal, 1) != 1) + return -EIO; + + if (uVal & 0x20) { + if (i2c_master_recv(client, &uTemp, 1) != 1) { + pr_err("[SSP]: %s - i2c recv fail\n", __func__); + return -EIO; + } + + if (i2c_master_recv(client, &uTemp, 1) != 1) { + pr_err("[SSP]: %s - i2c recv fail\n", __func__); + return -EIO; + } + + uVal &= ~0x20; + } + + if ((uVal & 0xF0) == BL_APP_CRC_FAIL) { + pr_info("[SSP] SSP_APP_CRC_FAIL - There is no bootloader.\n"); + if (i2c_master_recv(client, &uVal, 1) != 1) { + pr_err("[SSP]: %s - i2c recv fail\n", __func__); + return -EIO; + } + + if (uVal & 0x20) { + if (i2c_master_recv(client, &uTemp, 1) != 1) { + pr_err("[SSP]: %s - i2c recv fail\n", __func__); + return -EIO; + } + + if (i2c_master_recv(client, &uTemp, 1) != 1) { + pr_err("[SSP]: %s - i2c recv fail\n", __func__); + return -EIO; + } + + uVal &= ~0x20; + } + } + + switch (uState) { + case BL_WAITING_BOOTLOAD_CMD: + case BL_WAITING_FRAME_DATA: + uVal &= ~BL_BOOT_STATUS_MASK; + break; + case BL_FRAME_CRC_PASS: + if (uVal == BL_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (uVal != uState) { + pr_err("[SSP]: %s - Unvalid bootloader mode state\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int unlock_bootloader(struct i2c_client *client) +{ + u8 uBuf[2]; + + uBuf[0] = BL_UNLOCK_CMD_LSB; + uBuf[1] = BL_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, uBuf, 2) != 2) { + pr_err("[SSP]: %s - i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int fw_write(struct i2c_client *client, + const u8 *pData, unsigned int uFrameSize) +{ + if (i2c_master_send(client, pData, uFrameSize) != uFrameSize) { + pr_err("[SSP]: %s - i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int load_fw_bootmode(struct i2c_client *client, const char *pFn) +{ + const struct firmware *fw = NULL; + unsigned int uFrameSize; + unsigned int uPos = 0; + int iRet; + int iCheckFrameCrcError = 0; + int iCheckWatingFrameDataError = 0; + + pr_info("[SSP] ssp_load_fw start!!!\n"); + + iRet = request_firmware(&fw, pFn, &client->dev); + if (iRet) { + pr_err("[SSP]: %s - Unable to open firmware %s\n", + __func__, pFn); + return iRet; + } + + /* Unlock bootloader */ + unlock_bootloader(client); + + while (uPos < fw->size) { + iRet = check_bootloader(client, BL_WAITING_FRAME_DATA); + if (iRet) { + iCheckWatingFrameDataError++; + if (iCheckWatingFrameDataError > 10) { + pr_err("[SSP]: %s - F/W update fail\n", + __func__); + goto out; + } else { + pr_err("[SSP]: %s - F/W data_error %d, retry\n", + __func__, iCheckWatingFrameDataError); + continue; + } + } + + uFrameSize = ((*(fw->data + uPos) << 8) | + *(fw->data + uPos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + uFrameSize += 2; + + /* Write one frame to device */ + fw_write(client, fw->data + uPos, uFrameSize); + iRet = check_bootloader(client, BL_FRAME_CRC_PASS); + if (iRet) { + iCheckFrameCrcError++; + if (iCheckFrameCrcError > 10) { + pr_err("[SSP]: %s - F/W Update Fail. crc err\n", + __func__); + goto out; + } else { + pr_err("[SSP]: %s - F/W crc_error %d, retry\n", + __func__, iCheckFrameCrcError); + continue; + } + } + + uPos += uFrameSize; + + pr_info("[SSP] Updated %d bytes / %zd bytes\n", uPos, fw->size); + mdelay(1); + } + +out: + release_firmware(fw); + return iRet; +} + +static void change_to_bootmode(struct ssp_data *data) +{ + int iCnt = 0; + + for (iCnt = 0; iCnt < 10; iCnt++) { + data->set_mcu_reset(0); + udelay(10); + + data->set_mcu_reset(1); + msleep(100); + } + + msleep(50); +} + +void toggle_mcu_reset(struct ssp_data *data) +{ + u8 uBuf[2]; + + uBuf[0] = 0x00; + uBuf[1] = 0x00; + + data->set_mcu_reset(0); + udelay(10); + data->set_mcu_reset(1); + mdelay(50); + + data->client->addr = BOOTLOADER_SLAVE_ADDR; + + if (i2c_master_send(data->client, uBuf, 2) != 2) + pr_err("[SSP]: %s - ssp_Normal Mode\n", __func__); + else + pr_err("[SSP]: %s - ssp_load_fw_bootmode\n", __func__); + + data->client->addr = APP_SLAVE_ADDR; +} + +int update_mcu_bin(struct ssp_data *data) +{ + int iRet = 0; + + pr_info("[SSP] ssp_change_to_bootmode\n"); + change_to_bootmode(data); + data->client->addr = BOOTLOADER_SLAVE_ADDR; + iRet = load_fw_bootmode(data->client, BL_FW_NAME); + + msleep(SSP_SW_RESET_TIME); + + data->client->addr = APP_SLAVE_ADDR; + + if (iRet < 0) + data->bBinaryChashed = true; + + return iRet; +} + +int update_crashed_mcu_bin(struct ssp_data *data) +{ + int iRet = 0; + pr_info("[SSP] ssp_change_to_bootmode\n"); + change_to_bootmode(data); + data->client->addr = BOOTLOADER_SLAVE_ADDR; + iRet = load_fw_bootmode(data->client, BL_CRASHED_FW_NAME); + + msleep(SSP_SW_RESET_TIME); + + data->client->addr = APP_SLAVE_ADDR; + data->bBinaryChashed = true; + return iRet; +} + +void check_fwbl(struct ssp_data *data) +{ + int iRet; + + data->client->addr = BOOTLOADER_SLAVE_ADDR; + iRet = check_bootloader(data->client, BL_WAITING_BOOTLOAD_CMD); + + if (iRet >= 0) { + pr_info("[SSP] ssp_load_fw_bootmode\n"); + load_fw_bootmode(data->client, BL_FW_NAME); + msleep(SSP_SW_RESET_TIME); + } else { + data->client->addr = APP_SLAVE_ADDR; + data->uCurFirmRev = get_firmware_rev(data); + if (data->uCurFirmRev != SSP_FIRMWARE_REVISION) { + pr_info("[SSP] MPU Firm Rev. : Old = %8u, New = %8u\n", + data->uCurFirmRev, SSP_FIRMWARE_REVISION); + update_mcu_bin(data); + } + } + + data->client->addr = APP_SLAVE_ADDR; + data->uCurFirmRev = get_firmware_rev(data); + pr_info("[SSP] MPU Firm Rev. : Old = %8u, New = %8u\n", + data->uCurFirmRev, SSP_FIRMWARE_REVISION); +} |