aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sensorhub/ssp_firmware.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sensorhub/ssp_firmware.c')
-rw-r--r--drivers/sensorhub/ssp_firmware.c303
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);
+}