blob: 3a38a3c93e3a4642a2a85f688b92244317b85f4c [file] [log] [blame]
/*
* Copyright (C) 2013-2014 Motorola Mobility LLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/m4sensorhub.h>
#include <linux/slab.h>
#include <linux/m4sensorhub_stm32_bl_cmds.h>
#include <linux/m4sensorhub_notify.h>
/* --------------- Local Declarations -------------- */
/* We flash code in sector 0 to 255 */
/* From the Flash Programming Manual:
All sectors are 2K
*/
#define USER_FLASH_FIRST_PAGE_ADDRESS 0x08000000
#define VERSION_OFFSET 0x200
#define VERSION_ADDRESS (USER_FLASH_FIRST_PAGE_ADDRESS + VERSION_OFFSET)
#define FLASH_SIZE (1024*1024) /* 1 MB */
#define FLASH_SIZE_FOR_SW (FLASH_SIZE / 2)
#define BARKER_SIZE 8
/* Sector 255 ends at 0x0807FFFF, and we put BARKER_SIZE number of bytes as barker */
#define BARKER_ADDRESS (USER_FLASH_FIRST_PAGE_ADDRESS + FLASH_SIZE_FOR_SW - BARKER_SIZE)
#define USER_FLASH_BANK2_ADDRESS 0x08080000
#define USER_FLASH_BANK2_LAST_SECTOR 255
#define SECTOR_SIZE 0x800
#define HOST_CONFIG_SIZE 8
#define HOST_CONFIG_ADDRESS \
(USER_FLASH_BANK2_ADDRESS + \
(USER_FLASH_BANK2_LAST_SECTOR * SECTOR_SIZE))
#define HOST_CONFIG_SECTOR 511
/* The MAX_FILE_SIZE is the size of sectors 0 to 255 where the firmware code
* will reside (minus the barker size). */
#define MAX_FILE_SIZE (FLASH_SIZE_FOR_SW - BARKER_SIZE)
/* Transfer is done in 256 bytes */
/* L4 allows programming ONLY double words (2 * 32-bit data) */
#define MAX_TRANSFER_SIZE 256 /* bytes */
u8 barker_buffer[BARKER_SIZE] = {0xDE, 0xC0, 0xCE, 0x0A, 0x00, 0x00, 0x00, 0x00};
/* -------------- Local Functions ------------- */
static int m4sensorhub_update_host_config(struct m4sensorhub_data *m4sensorhub)
{
int ret = -1;
int host_config_sector = HOST_CONFIG_SECTOR;
int num_of_sectors_host_config = 1;
u64 host_config_from_device;
if (m4sensorhub_bl_rm(m4sensorhub, HOST_CONFIG_ADDRESS,
(u8 *)&host_config_from_device,
HOST_CONFIG_SIZE) != 0) {
KDEBUG(M4SH_ERROR, "%s : %d : error reading host_config\n",
__func__, __LINE__);
return -EIO;
}
if (host_config_from_device == m4sensorhub->host_config) {
KDEBUG(M4SH_NOTICE, "%s: host_config match\n", __func__);
return 0;
}
KDEBUG(M4SH_NOTICE, "host config differ\n");
if (m4sensorhub_bl_erase_sectors(
m4sensorhub, &host_config_sector,
num_of_sectors_host_config) != 0) {
KDEBUG(M4SH_ERROR, "%s : %d : error erasing sectors\n",
__func__, __LINE__);
}
if (m4sensorhub_bl_wm(m4sensorhub,
HOST_CONFIG_ADDRESS,
(u8 *)&(m4sensorhub->host_config),
HOST_CONFIG_SIZE) != 0) {
KDEBUG(M4SH_ERROR,
"%s : %d: error writing host_config\n",
__func__, __LINE__);
}
return ret;
}
static int m4sensorhub_bl_jump_to_user(struct m4sensorhub_data *m4sensorhub)
{
int ret = -1;
u8 barker_read_from_device[BARKER_SIZE];
int loop;
if (m4sensorhub_bl_rm(m4sensorhub, BARKER_ADDRESS,
barker_read_from_device, BARKER_SIZE) != 0) {
KDEBUG(M4SH_ERROR, "%s : %d : error reading from device\n",
__func__, __LINE__);
KDEBUG(M4SH_ERROR, "*** Not executing M4 code ***\n");
return -EIO;
}
if (memcmp(barker_read_from_device, barker_buffer, BARKER_SIZE) == 0) {
/* check if host_config is properly written
* before letting M4 run */
/* deliberately ignoring return value from this function */
m4sensorhub_update_host_config(m4sensorhub);
m4sensorhub_bl_go(m4sensorhub, USER_FLASH_FIRST_PAGE_ADDRESS);
KDEBUG(M4SH_NOTICE, "Waiting for M4 setup\n");
/* M4 takes about 1.47s to boot up with our binary running,
adding some cushion and sleeping for 2.5s */
msleep(2500);
KDEBUG(M4SH_NOTICE, "Executing M4 code\n");
ret = 0;
} else {
KDEBUG(M4SH_ERROR, "Wrong Barker Number read \n");
for (loop = 0; loop < BARKER_SIZE; loop++) {
KDEBUG(M4SH_ERROR, "read is 0x%02x ", barker_read_from_device[loop]);
KDEBUG(M4SH_ERROR, "expected is 0x%02x ", barker_buffer[loop]);
}
KDEBUG(M4SH_ERROR, "\n");
KDEBUG(M4SH_ERROR, "*** Not executing M4 code ***\n");
}
return ret;
}
static int m4sensorhub_bl_erase_fw(struct m4sensorhub_data *m4sensorhub)
{
return m4sensorhub_bl_erase_bank(m4sensorhub, BANK1);
}
/* -------------- Global Functions ----------------- */
/* m4sensorhub_l4_load_firmware()
This function uses I2C bootloader version 1.1 on L4
Check firmware and load if different from what's already on the L4.
Then jump to user code on L4.
Returns 0 on success or negative error code on failure
m4sensorhub - pointer to the main m4sensorhub data struct
*/
#define FLASHING_RETRIES 5
int m4sensorhub_l4_load_firmware(struct m4sensorhub_data *m4sensorhub,
unsigned short force_upgrade, const struct firmware *fm)
{
const struct firmware *firmware = fm;
int ret = -EINVAL;
int bytes_left, bytes_to_write;
int address_to_write;
u8 *buf_to_read, *buf = NULL;
u8 *write_buf;
u16 fw_version_file, fw_version_device;
u8 barker_read_from_device[BARKER_SIZE];
int loop;
int iter;
bool writefailed = false;
if (m4sensorhub == NULL) {
KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__);
ret = -ENODATA;
goto done;
}
/* Always write MAX_TRANSFER_SIZE, even for the last chunk*/
buf = kzalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);
if (!buf) {
KDEBUG(M4SH_ERROR, "%s: Failed to allocate buf\n", __func__);
ret = -ENOMEM;
goto done;
}
if (firmware == NULL) {
ret = request_firmware(&firmware, m4sensorhub->filename,
&m4sensorhub->i2c_client->dev);
if (ret < 0) {
KDEBUG(M4SH_ERROR, "%s: req fw failed %s, %d\n",
__func__, m4sensorhub->filename, ret);
KDEBUG(M4SH_ERROR, "run fw already on hw.\n");
ret = 0;
goto done;
}
}
KDEBUG(M4SH_INFO, "%s: Filename = %s", __func__, m4sensorhub->filename);
if (firmware->size > MAX_FILE_SIZE) {
KDEBUG(M4SH_ERROR, "%s: firmware file size is too big.\n",
__func__);
ret = -EINVAL;
goto done;
}
fw_version_file = *(u16 *)(firmware->data +
VERSION_ADDRESS - USER_FLASH_FIRST_PAGE_ADDRESS);
for (iter = 0; iter < FLASHING_RETRIES; iter++) {
m4sensorhub_hw_reset(m4sensorhub);
KDEBUG(M4SH_INFO, "%s: iter = %d\n", __func__, iter);
if (!force_upgrade) {
/* Verify Barker number from device */
if (m4sensorhub_bl_rm(m4sensorhub, BARKER_ADDRESS,
barker_read_from_device,
BARKER_SIZE) != 0) {
KDEBUG(M4SH_ERROR, "%s : %d : Barker fail\n",
__func__, __LINE__);
continue;
}
if (memcmp(barker_read_from_device,
barker_buffer, BARKER_SIZE) != 0) {
KDEBUG(M4SH_NOTICE, "Barker does not match\n");
for (loop = 0; loop < BARKER_SIZE; loop++) {
KDEBUG(M4SH_ERROR, "read 0x%02x ",
barker_read_from_device[loop]);
KDEBUG(M4SH_ERROR, "expected 0x%02x ",
barker_buffer[loop]);
}
KDEBUG(M4SH_ERROR, "\n");
KDEBUG(M4SH_NOTICE,
"forcing firmware update from file\n");
} else {
/* Read firmware version from device */
if (m4sensorhub_bl_rm(m4sensorhub,
VERSION_ADDRESS,
(u8 *)&fw_version_device,
sizeof(fw_version_device)) != 0) {
KDEBUG(M4SH_ERROR, "Read err at ");
KDEBUG(M4SH_ERROR, "%s : %d :\n",
__func__, __LINE__);
continue;
}
if (fw_version_file == fw_version_device) {
KDEBUG(M4SH_NOTICE,
"Version of firmware on device is 0x%04x\n",
fw_version_device);
KDEBUG(M4SH_NOTICE,
"Firmware on device same as file, not loading firmware.\n");
ret = 0;
goto done;
}
/* Print statement below isn't
* really an ERROR, but
* this ensures it is always printed */
KDEBUG(M4SH_ERROR,
"Version of firmware on device is 0x%04x\n",
fw_version_device);
KDEBUG(M4SH_ERROR,
"Version of firmware on file is 0x%04x\n",
fw_version_file);
KDEBUG(M4SH_ERROR,
"Firmware on device different from file, updating...\n");
}
} else {
KDEBUG(M4SH_NOTICE, "firmware from file is 0x%04x\n",
fw_version_file);
}
/* The flash memory to update has
* to be erased before updating */
ret = m4sensorhub_bl_erase_fw(m4sensorhub);
if (ret < 0) {
KDEBUG(M4SH_ERROR, "%s: erase failed\n", __func__);
/* TODO : Not sure if this is critical error
goto done; */
}
bytes_left = firmware->size;
address_to_write = USER_FLASH_FIRST_PAGE_ADDRESS;
buf_to_read = (u8 *)firmware->data;
KDEBUG(M4SH_DEBUG, "%s: %d bytes to be written\n", __func__,
(int)firmware->size);
while (bytes_left) {
if (bytes_left > MAX_TRANSFER_SIZE) {
bytes_to_write = MAX_TRANSFER_SIZE;
write_buf = buf_to_read;
} else {
bytes_to_write = bytes_left;
memcpy(buf, buf_to_read, bytes_to_write);
write_buf = buf;
}
writefailed = false;
if (m4sensorhub_bl_wm(m4sensorhub, address_to_write,
write_buf,
MAX_TRANSFER_SIZE) != 0) {
KDEBUG(M4SH_ERROR, "write err addr 0x%08x at ",
address_to_write);
KDEBUG(M4SH_ERROR, " %s : %d\n",
__func__, __LINE__);
writefailed = true;
break;
}
/* Relying on CRC to take care of errors */
address_to_write += bytes_to_write;
buf_to_read += bytes_to_write;
bytes_left -= bytes_to_write;
}
if (writefailed == true)
continue;
/* Write barker number when firmware successfully written */
if (m4sensorhub_bl_wm(m4sensorhub, BARKER_ADDRESS,
barker_buffer, BARKER_SIZE) != 0) {
KDEBUG(M4SH_ERROR, "%s : %d : error writing\n",
__func__, __LINE__);
continue;
}
KDEBUG(M4SH_NOTICE, "%s: %d bytes written successfully\n",
__func__, (int)firmware->size);
ret = 0;
break;
}
done:
/* If ret is invalid, then we don't try to jump to user code */
if (ret >= 0 && m4sensorhub_bl_jump_to_user(m4sensorhub) < 0)
/* If jump to user code fails, return failure */
ret = -EINVAL;
KDEBUG(M4SH_INFO, "%s: enabling peripherals\n", __func__);
m4sensorhub_notify_subscriber(ENABLE_PERIPHERAL);
release_firmware(firmware);
kfree(buf);
/* irrespective of whether we upgraded FW or not,
the firmware version will be the same as one in the file*/
m4sensorhub->fw_version = fw_version_file;
return ret;
}
EXPORT_SYMBOL_GPL(m4sensorhub_l4_load_firmware);
/* TODO: Restructure reflash code so that this function can go in the core */
int m4sensorhub_test_m4_reboot(struct m4sensorhub_data *m4, bool reboot_first)
{
int err = 0;
int i;
unsigned char buf[2];
if (m4 == NULL) {
KDEBUG(M4SH_ERROR, "%s: M4 data is missing.\n", __func__);
err = -ENODATA;
goto m4sensorhub_test_m4_reboot_exit;
}
for (i = 0; i < 3; i++) {
if ((i > 0) || (reboot_first)) {
m4sensorhub_hw_reset(m4);
err = m4sensorhub_bl_jump_to_user(m4);
if (err < 0) {
KDEBUG(M4SH_ERROR,
"%s: M4 reboot failed (retries=%d)\n",
__func__, i);
continue;
}
}
/* Wait progressively longer for M4 to become ready */
if (i > 0)
msleep(i * 100);
/* Read M4 register to test if M4 is ready */
buf[0] = 0x00; /* Bank */
buf[1] = 0x00; /* Offset */
err = m4sensorhub_i2c_write_read(m4, &(buf[0]), 2, 1);
if (err < 0) {
KDEBUG(M4SH_ERROR, "%s: %s (retries=%d).\n", __func__,
"Failed initial I2C read", i);
continue;
} else {
/* No failure so break loop */
err = 0;
break;
}
}
if (err < 0)
panic("%s: M4 has failed--forcing panic...\n", __func__);
m4sensorhub_test_m4_reboot_exit:
return err;
}
EXPORT_SYMBOL_GPL(m4sensorhub_test_m4_reboot);