aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sensorhub/stm/ssp_firmware.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sensorhub/stm/ssp_firmware.c')
-rwxr-xr-xdrivers/sensorhub/stm/ssp_firmware.c709
1 files changed, 709 insertions, 0 deletions
diff --git a/drivers/sensorhub/stm/ssp_firmware.c b/drivers/sensorhub/stm/ssp_firmware.c
new file mode 100755
index 00000000000..3b077385f52
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_firmware.c
@@ -0,0 +1,709 @@
+/*
+ * 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 <mach/gpio.h>
+
+#include "ssp.h"
+
+#define SSP_FIRMWARE_REVISION_STM 13061300 /*MCU L5, 6500*/
+
+/* Bootload mode cmd */
+#define BL_FW_NAME "ssp_b.fw"
+#define BL_UMS_FW_NAME "ssp_b.bin"
+#define BL_CRASHED_FW_NAME "ssp_crashed.fw"
+
+#define BL_UMS_FW_PATH 255
+
+#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
+
+/* STM */
+#define SSP_STM_DEBUG 0
+
+#define SEND_ADDR_LEN 5
+#define BL_SPI_SOF 0x5A
+#define BL_ACK 0x79
+#define BL_NACK 0x1F
+#define DEF_ACK_CMD_RETRIES 3000
+#define STM_MAX_XFER_SIZE 256
+#define STM_MAX_BUFFER_SIZE 260
+#define STM_APP_ADDR 0x08000000
+
+#define WMEM_COMMAND 0x31 /* Write Memory command */
+#define GO_COMMAND 0x21 /* GO command */
+#define EXT_ER_COMMAND 0x44 /* Erase Memory command */
+
+#define XOR_WMEM_COMMAND 0xCE /* Write Memory command */
+#define XOR_GO_COMMAND 0xDE /* GO command */
+#define XOR_EXT_ER_COMMAND 0xBB /* Erase Memory command */
+
+#define EXT_ER_DATA_LEN 3
+
+struct stm32fwu_spi_cmd {
+ u8 cmd;
+ u8 xor_cmd;
+ u8 ack_pad; /* Send this when waiting for an ACK */
+ u8 reserved;
+ int status; /* ACK or NACK (or error) */
+ int timeout; /* This is number of retries */
+ int ack_loops;
+};
+
+unsigned int get_module_rev(struct ssp_data *data)
+{
+ return SSP_FIRMWARE_REVISION_STM;
+}
+
+void stm32_set_ss(unsigned int gpio, int enable)
+{
+ mdelay(1);
+ gpio_set_value(gpio, enable);
+ mdelay(1);
+}
+
+static int stm32fwu_spi_wait_for_ack(struct spi_device *spi,
+ struct stm32fwu_spi_cmd *cmd, u8 dummy_bytes)
+{
+ struct spi_message m;
+ char tx_buf = 0x0;
+ char rx_buf = 0x0;
+ struct spi_transfer t = {
+ .tx_buf = &tx_buf,
+ .rx_buf = &rx_buf,
+ .len = 1,
+ .bits_per_word = 8,
+ };
+ int i = 0;
+ int ret;
+
+#if SSP_STM_DEBUG
+ pr_info("[SSP] %s: dummy byte = 0x%02hhx\n",
+ __func__, dummy_bytes);
+#endif
+ while (i < cmd->timeout) {
+ tx_buf = dummy_bytes;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if ((rx_buf == BL_ACK) || (rx_buf == BL_NACK)) {
+ cmd->ack_loops = i;
+ usleep_range(1000, 1100);
+ return (int)rx_buf;
+ }
+ usleep_range(1000, 1100);
+ i++;
+ }
+#if SSP_STM_DEBUG
+ dev_err(&spi->dev, "%s: Timeout after %d loops\n",
+ __func__, cmd->timeout);
+#endif
+ return -EIO;
+}
+
+static int stm32fwu_spi_send_cmd(struct spi_device *spi,
+ struct stm32fwu_spi_cmd *cmd)
+{
+ u8 tx_buf[3] = {0,};
+ u8 rx_buf[3] = {0,};
+ u8 dummy_byte = 0;
+ struct spi_message m;
+ struct spi_transfer t = {
+ .tx_buf = tx_buf,
+ .rx_buf = rx_buf,
+ .len = 3,
+ .bits_per_word = 8,
+ };
+ int ret;
+
+ spi_message_init(&m);
+
+ tx_buf[0] = BL_SPI_SOF;
+ tx_buf[1] = cmd->cmd;
+ tx_buf[2] = cmd->xor_cmd;
+
+ spi_message_add_tail(&t, &m);
+
+#if SSP_STM_DEBUG
+ pr_info("[SSP] tx buffer for cmd is: 0x%02hx 0x%02hx 0x%02hx\n",
+ tx_buf[0], tx_buf[1], tx_buf[2]);
+#endif
+ ret = spi_sync(spi, &m);
+
+ if ((rx_buf[0] == BL_ACK) || (rx_buf[0] == BL_NACK)) {
+#if SSP_STM_DEBUG
+ pr_info("Found Cmd Ack/NAK (0x%02hhX on first read\n",
+ rx_buf[0]);
+#endif
+ cmd->ack_loops = 0;
+ return (int)rx_buf[0];
+ }
+
+ usleep_range(5000, 5500);
+ dummy_byte = cmd->ack_pad;
+
+ /* check for ack/nack and loop until found */
+ ret = stm32fwu_spi_wait_for_ack(spi, cmd, dummy_byte);
+ cmd->status = ret;
+
+ if (ret != BL_ACK) {
+ pr_err("[SSP] %s: Got NAK or Error %d\n", __func__, ret);
+ return ret;
+ }
+#if SSP_STM_DEBUG
+ pr_info("[SSP] Got 0x%02hX for ack after sending cmd 0x%02hX\n",
+ ret, cmd->cmd);
+#endif
+
+ return ret;
+}
+
+static int stm32fwu_spi_write(struct spi_device *spi,
+ const u8 *buffer, ssize_t len)
+{
+ int ret;
+ u8 rx_buf[STM_MAX_BUFFER_SIZE] = {0,};
+ struct spi_message m;
+ struct spi_transfer t = {
+ .tx_buf = buffer,
+ .rx_buf = rx_buf,
+ .len = len,
+ .bits_per_word = 8,
+ };
+
+ if (len > STM_MAX_BUFFER_SIZE) {
+ pr_err("[SSP] Can't send more than 256 bytes at a time\n");
+ return -EINVAL;
+ }
+#if SSP_STM_DEBUG
+ pr_info("[SSP] Writing %d bytes from buffer @ %p\n", len, buffer);
+#endif
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+#if SSP_STM_DEBUG
+ pr_info("[SSP] %s: spi_sync() returned %d\n", __func__, ret);
+#endif
+
+ if (ret < 0) {
+ pr_err("[SSP] Error in %d spi_read()\n", ret);
+ return ret;
+ }
+
+ return len;
+}
+
+static int send_addr(struct spi_device *spi, u32 fw_addr, int send_short)
+{
+ int res;
+ int i = send_short;
+ int len = SEND_ADDR_LEN - send_short;
+ u8 header[SEND_ADDR_LEN];
+ struct stm32fwu_spi_cmd dummy_cmd;
+ dummy_cmd.timeout = 1000;
+
+
+ header[0] = (u8)((fw_addr >> 24) & 0xFF);
+ header[1] = (u8)((fw_addr >> 16) & 0xFF);
+ header[2] = (u8)((fw_addr >> 8) & 0xFF);
+ header[3] = (u8)(fw_addr & 0xFF);
+ header[4] = header[0] ^ header[1] ^ header[2] ^ header[3];
+ if ((i < 0) || (i > 1)) {
+ pr_err("[SSP] Send short flag must be 0 or 1.\n");
+ return -EINVAL;
+ }
+
+ res = stm32fwu_spi_write(spi, &header[i], len);
+
+ if (res < len) {
+ pr_err("[SSP] Error in sending address. Res %d\n", res);
+ return ((res > 0) ? -EIO : res);
+ }
+#if SSP_STM_DEBUG
+ pr_info("[SSP] send_addr(): wrote addr OK\n");
+#endif
+ res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, 0x79);
+ if (res != BL_ACK) {
+ pr_err("[SSP] send_addr(): rcv_ack returned 0x%x\n",
+ res);
+ return res;
+ }
+ return 0;
+}
+
+static int fw_write_stm(struct spi_device *spi, u32 fw_addr,
+ int len, const u8 *buffer)
+{
+ int res;
+ struct stm32fwu_spi_cmd cmd;
+ struct stm32fwu_spi_cmd dummy_cmd;
+ int i;
+ u8 xor = 0;
+ u8 send_buff[STM_MAX_BUFFER_SIZE] = {0,};
+
+ cmd.cmd = WMEM_COMMAND;
+ cmd.xor_cmd = XOR_WMEM_COMMAND;
+ cmd.timeout = 1000;
+ cmd.ack_pad = (u8)((fw_addr >> 24) & 0xFF);
+ dummy_cmd.timeout = 1000;
+
+#if SSP_STM_DEBUG
+ pr_info("[SSP] sending WMEM_COMMAND\n");
+#endif
+
+ if (len > STM_MAX_XFER_SIZE) {
+ pr_err("[SSP] Can't send more than 256 bytes per "
+ "transaction\n");
+ return -EINVAL;
+ }
+
+ send_buff[0] = len - 1;
+ memcpy(&send_buff[1], buffer, len);
+ for (i = 0; i < (len + 1); i++)
+ xor ^= send_buff[i];
+
+ send_buff[len + 1] = xor;
+
+ res = stm32fwu_spi_send_cmd(spi, &cmd);
+ if (res != BL_ACK) {
+ pr_err("[SSP] Error %d sending read_mem cmd\n", res);
+ return res;
+ }
+
+ if (cmd.ack_loops > 0)
+ res = send_addr(spi, fw_addr, 1);
+ else
+ res = send_addr(spi, fw_addr, 0);
+
+ if (res != 0) {
+ pr_err("[SSP] Error %d sending write_mem Address\n", res);
+ return res;
+ }
+
+ res = stm32fwu_spi_write(spi, send_buff, len + 2);
+ if (res < len) {
+ pr_err("[SSP] Error writing to flash. res = %d\n", res);
+ return ((res > 0) ? -EIO : res);
+ }
+
+ for (i = 0; i < 5; i++) {
+ res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, 0x79);
+ if (res == BL_ACK)
+ return len;
+
+ if (res == BL_NACK) {
+ pr_err("[SSP] Got NAK waiting for WRITE_MEM "
+ "to complete\n");
+ return res;
+ }
+ }
+ pr_err("[SSP] timeout waiting for ACK for WRITE_MEM command\n");
+
+ return -ETIME;
+}
+
+static int load_ums_fw_bootmode(struct spi_device *spi, const char *pFn)
+{
+ const u8 *buff = NULL;
+ int remaining;
+ char fw_path[BL_UMS_FW_PATH+1];
+ unsigned int uPos = 0;
+ unsigned int fw_addr = STM_APP_ADDR;
+ int iRet = SUCCESS;
+ int block = STM_MAX_XFER_SIZE;
+ int count = 0;
+ int err_count = 0;
+ int retry_count = 0;
+ unsigned int uFSize = 0, uNRead = 0;
+ struct file *fp = NULL;
+ mm_segment_t old_fs = get_fs();
+
+ pr_info("[SSP] ssp_load_ums_fw start!!!\n");
+ set_fs(get_ds());
+
+ snprintf(fw_path, BL_UMS_FW_PATH, "/sdcard/ssp/%s", pFn);
+
+ fp = filp_open(fw_path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ iRet = ERROR;
+ pr_err("file %s open error:%d\n", fw_path, (s32)fp);
+ goto err_open;
+ }
+
+ uFSize = (unsigned int)fp->f_path.dentry->d_inode->i_size;
+ pr_info("ssp_load_ums firmware size: %u\n", uFSize);
+
+ buff = kzalloc((size_t)uFSize, GFP_KERNEL);
+ if (!buff) {
+ iRet = ERROR;
+ pr_err("fail to alloc buffer for fw\n");
+ goto err_alloc;
+ }
+
+ uNRead = (unsigned int)vfs_read(fp, (char __user *)buff,
+ (unsigned int)uFSize, &fp->f_pos);
+ if (uNRead != uFSize) {
+ iRet = ERROR;
+ pr_err("fail to read file %s (nread = %u)\n", fw_path, uNRead);
+ goto err_fw_size;
+ }
+
+ remaining = (int)uFSize;
+ while (remaining > 0) {
+ if (block > remaining)
+ block = remaining;
+
+ while (retry_count < 3) {
+ iRet = fw_write_stm(spi, fw_addr, block, buff + uPos);
+ if (iRet < block) {
+ pr_err("[SSP] Error writing to addr 0x%08X\n",
+ fw_addr);
+ if (iRet < 0) {
+ pr_err("[SSP] Erro was %d\n", iRet);
+ } else {
+ pr_err("[SSP] Incomplete write of %d "
+ "bytes\n", iRet);
+ iRet = -EIO;
+ }
+ retry_count++;
+ err_count++;
+ } else {
+ retry_count = 0;
+ break;
+ }
+ }
+ if (iRet < 0)
+ pr_err("[SSP] Writing MEM failed: %d, retry cont: %d\n",
+ iRet, err_count);
+
+ remaining -= block;
+ uPos += block;
+ fw_addr += block;
+ if (count++ == 20) {
+ pr_info("[SSP] Updated %u bytes / %u bytes\n", uPos,
+ uFSize);
+ count = 0;
+ }
+ }
+
+ pr_info("[SSP] Firmware download is success.(%d bytes, retry %d)\n",
+ uPos, err_count);
+
+out:
+err_fw_size:
+ kfree(buff);
+err_alloc:
+ filp_close(fp, NULL);
+err_open:
+ set_fs(old_fs);
+ return iRet;
+}
+
+static int fw_erase_stm(struct spi_device *spi)
+{
+ struct stm32fwu_spi_cmd cmd;
+ struct stm32fwu_spi_cmd dummy_cmd;
+ int ret;
+ char buff[EXT_ER_DATA_LEN] = {0xff, 0xff, 0x00};
+
+ cmd.cmd = EXT_ER_COMMAND;
+ cmd.xor_cmd = XOR_EXT_ER_COMMAND;
+ cmd.timeout = 1000;
+ cmd.ack_pad = 0xFF;
+ dummy_cmd.timeout = DEF_ACK_CMD_RETRIES;
+
+ ret = stm32fwu_spi_send_cmd(spi, &cmd);
+
+ if (ret < 0 || ret != BL_ACK) {
+ pr_err("[SSP] fw_erase failed\n");
+ return -EIO;
+ }
+ if (cmd.ack_loops == 0)
+ ret = stm32fwu_spi_write(spi, buff, EXT_ER_DATA_LEN);
+ else
+ ret = stm32fwu_spi_write(spi, buff, EXT_ER_DATA_LEN-1);
+
+#if SSP_STM_DEBUG
+ pr_info("[SSP] %s: stm32fwu_spi_write(sync byte) returned %d\n",
+ __func__, ret);
+#endif
+
+ ret = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, 0x79);
+
+#if SSP_STM_DEBUG
+ pr_info("[SSP] %s: stm32fwu_spi_wait_for_ack returned %d (0x%x)\n",
+ __func__, ret, ret);
+#endif
+
+ return ret;
+
+}
+static int load_kernel_fw_bootmode(struct spi_device *spi, const char *pFn)
+{
+ const struct firmware *fw = NULL;
+ int remaining;
+ unsigned int uPos = 0;
+ unsigned int fw_addr = STM_APP_ADDR;
+ int iRet = SUCCESS;
+ int block = STM_MAX_XFER_SIZE;
+ int count = 0;
+ int err_count = 0;
+ int retry_count = 0;
+
+ pr_info("[SSP] ssp_load_fw start!!!\n");
+
+ iRet = request_firmware(&fw, pFn, &spi->dev);
+ if (iRet) {
+ pr_err("[SSP] Unable to open firmware %s\n", pFn);
+ return iRet;
+ }
+
+ remaining = fw->size;
+ while (remaining > 0) {
+ if (block > remaining)
+ block = remaining;
+
+ while (retry_count < 3) {
+ iRet = fw_write_stm(spi, fw_addr, block,
+ fw->data + uPos);
+ if (iRet < block) {
+ pr_err("[SSP] Error writing to addr 0x%08X\n",
+ fw_addr);
+ if (iRet < 0) {
+ pr_err("[SSP] Erro was %d\n", iRet);
+ } else {
+ pr_err("[SSP] Incomplete write of %d "
+ "bytes\n", iRet);
+ iRet = -EIO;
+ }
+ retry_count++;
+ err_count++;
+ } else {
+ retry_count = 0;
+ break;
+ }
+ }
+ if (iRet < 0) {
+ pr_err("[SSP] Writing MEM failed: %d, retry cont: %d\n",
+ iRet, err_count);
+ }
+ remaining -= block;
+ uPos += block;
+ fw_addr += block;
+ if (count++ == 20) {
+ pr_info("[SSP] Updated %u bytes / %u bytes\n", uPos,
+ fw->size);
+ count = 0;
+ }
+ }
+
+ pr_info("[SSP] Firmware download is success.(%d bytes, retry %d)\n",
+ uPos, err_count);
+
+out:
+ release_firmware(fw);
+ return iRet;
+}
+
+static void change_to_bootmode(struct ssp_data *data)
+{
+ int iCnt;
+ int ret;
+ char syncb = BL_SPI_SOF;
+ struct stm32fwu_spi_cmd dummy_cmd;
+
+ dummy_cmd.timeout = DEF_ACK_CMD_RETRIES;
+
+ data->set_mcu_reset(0);
+ mdelay(4);
+ data->set_mcu_reset(1);
+ usleep_range(45000, 47000);
+
+ for (iCnt = 0; iCnt < 9; iCnt++) {
+ data->set_mcu_reset(0);
+ usleep_range(4000, 4200);
+ data->set_mcu_reset(1);
+ usleep_range(15000, 15500);
+ }
+
+ ret = stm32fwu_spi_write(data->spi, &syncb, 1);
+#if SSP_STM_DEBUG
+ pr_info("[SSP] stm32fwu_spi_write(sync byte) returned %d\n", ret);
+#endif
+ ret = stm32fwu_spi_wait_for_ack(data->spi, &dummy_cmd, 0x79);
+#if SSP_STM_DEBUG
+ pr_info("[SSP] stm32fwu_spi_wait_for_ack returned %d (0x%x)\n",
+ ret, ret);
+#endif
+}
+
+void toggle_mcu_reset(struct ssp_data *data)
+{
+ data->set_mcu_reset(0);
+ usleep_range(1000, 1200);
+ data->set_mcu_reset(1);
+ msleep(50);
+}
+
+static int update_mcu_bin(struct ssp_data *data, int iBinType)
+{
+ int iRet = SUCCESS;
+ struct stm32fwu_spi_cmd cmd;
+
+ cmd.cmd = GO_COMMAND;
+ cmd.xor_cmd = XOR_GO_COMMAND;
+ cmd.timeout = 1000;
+ cmd.ack_pad = (u8)((STM_APP_ADDR >> 24) & 0xFF);
+
+ pr_info("[SSP] ssp_change_to_bootmode\n");
+
+ change_to_bootmode(data);
+
+ iRet = fw_erase_stm(data->spi);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - fw_erase_stm %d\n",
+ __func__, iRet);
+ return iRet;
+ }
+
+ /* After erase put bootmode again: debug */
+ change_to_bootmode(data);
+
+ switch (iBinType) {
+ case KERNEL_BINARY:
+ iRet = load_kernel_fw_bootmode(data->spi,
+ BL_FW_NAME);
+ break;
+
+ case KERNEL_CRASHED_BINARY:
+ iRet = load_kernel_fw_bootmode(data->spi,
+ BL_CRASHED_FW_NAME);
+ break;
+ case UMS_BINARY:
+ iRet = load_ums_fw_bootmode(data->spi,
+ BL_UMS_FW_NAME);
+ break;
+
+ default:
+ pr_err("[SSP] binary type error!!\n");
+ }
+
+ /* STM : GO USER ADDR */
+ stm32fwu_spi_send_cmd(data->spi, &cmd);
+ if (cmd.ack_loops > 0)
+ send_addr(data->spi, STM_APP_ADDR, 1);
+ else
+ send_addr(data->spi, STM_APP_ADDR, 0);
+
+ msleep(SSP_SW_RESET_TIME);
+
+ return iRet;
+}
+
+int forced_to_download_binary(struct ssp_data *data, int iBinType)
+{
+ int iRet = 0;
+ int retry = 3;
+
+ ssp_dbg("[SSP]: %s - mcu binany update!\n", __func__);
+
+ ssp_enable(data, false);
+
+ data->fw_dl_state = FW_DL_STATE_DOWNLOADING;
+ pr_info("[SSP] %s, DL state = %d\n", __func__,
+ data->fw_dl_state);
+ do {
+ pr_info("[SSP] %d try\n", 3 - retry);
+ iRet = update_mcu_bin(data, iBinType);
+ } while (retry -- > 0 && iRet < 0);
+
+ if (iRet < 0) {
+ ssp_dbg("[SSP]: %s - update_mcu_bin failed!\n", __func__);
+ goto out;
+ }
+
+ data->fw_dl_state = FW_DL_STATE_SYNC;
+ pr_info("[SSP] %s, DL state = %d\n", __func__, data->fw_dl_state);
+ iRet = initialize_mcu(data);
+ if (iRet < 0) {
+ iRet = ERROR;
+ ssp_dbg("[SSP]: %s - initialize_mcu failed!\n", __func__);
+ goto out;
+ }
+
+ ssp_enable(data, true);
+ sync_sensor_state(data);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_RESET);
+#endif
+
+ data->fw_dl_state = FW_DL_STATE_DONE;
+ pr_info("[SSP] %s, DL state = %d\n", __func__, data->fw_dl_state);
+
+ iRet = SUCCESS;
+out:
+ return iRet;
+}
+
+static unsigned int check_firmware_rev(struct ssp_data *data)
+{
+ char chTxData = MSG2SSP_AP_FIRMWARE_REV;
+ char chRxBuf[3] = {0,};
+ unsigned int uRev = SSP_INVALID_REVISION;
+ int iRet;
+
+ iRet = ssp_read_data(data, &chTxData, 1, chRxBuf, 3, DEFAULT_RETRIES);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - spi fail %d\n", __func__, iRet);
+ } else
+ uRev = ((unsigned int)chRxBuf[0] << 16)
+ | ((unsigned int)chRxBuf[1] << 8) | chRxBuf[2];
+ return uRev;
+}
+
+int check_fwbl(struct ssp_data *data)
+{
+ unsigned int fw_revision;
+
+ fw_revision = SSP_FIRMWARE_REVISION_STM;
+ data->uCurFirmRev = check_firmware_rev(data);
+
+ if (data->uCurFirmRev == SSP_INVALID_REVISION) {
+ pr_err("[SSP] SSP_INVALID_REVISION\n");
+ return FW_DL_STATE_NEED_TO_SCHEDULE;
+
+ } else {
+ pr_info("[SSP] MCU Firm Rev : Old = %8u, New = %8u\n",
+ data->uCurFirmRev, fw_revision);
+
+ if (data->uCurFirmRev != fw_revision)
+ return FW_DL_STATE_NEED_TO_SCHEDULE;
+ }
+
+ return FW_DL_STATE_NONE;
+}