| /* |
| * Copyright (C) 2012-2014 Motorola, 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 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> |
| |
| /* --------------- Global Declarations -------------- */ |
| |
| /* ------------ Local Function Prototypes ----------- */ |
| static int m4sensorhub_jump_to_user(struct m4sensorhub_data *m4sensorhub); |
| |
| /* --------------- Local Declarations -------------- */ |
| /* The M4 Flash Memory Map |
| * From the Flash Programming Manual: |
| Sector Start Address Size comments |
| ------ ------------- ---------- ---------------------------- |
| 0 0x08000000 16 Kbytes reserved for M4 bootload |
| 1 0x08004000 16 Kbytes first M4 firmware code page |
| 2 0x08008000 16 Kbytes |
| 3 0x0800C000 16 Kbytes |
| 4 0x08010000 64 Kbytes |
| 5 0x08020000 128 Kbytes last M4 firmware code page |
| 6 0x08040000 128 Kbytes first M4 file system page |
| 7 0x08060000 128 Kbytes |
| 8 0x08080000 128 Kbytes |
| 9 0x080A0000 128 Kbytes |
| 10 0x080C0000 128 Kbytes |
| 11 0x080E0000 128 Kbytes last M4 file system page |
| */ |
| enum { |
| M4_FLASH_SECTOR0 = 0x08000000, |
| M4_FLASH_SECTOR1 = 0x08004000, |
| M4_FLASH_SECTOR2 = 0x08008000, |
| M4_FLASH_SECTOR3 = 0x0800C000, |
| M4_FLASH_SECTOR4 = 0x08010000, |
| M4_FLASH_SECTOR5 = 0x08020000, |
| M4_FLASH_SECTOR6 = 0x08040000, |
| M4_FLASH_SECTOR7 = 0x08060000, |
| M4_FLASH_SECTOR8 = 0x08080000, |
| M4_FLASH_SECTOR9 = 0x080A0000, |
| M4_FLASH_SECTORA = 0x080C0000, |
| M4_FLASH_SECTORB = 0x080E0000, |
| M4_FLASH_END = 0x08100000 |
| }; |
| #define USER_FLASH_FIRST_PAGE_ADDRESS M4_FLASH_SECTOR1 |
| #define USER_FLASH_FIRST_FILE_ADDRESS M4_FLASH_SECTOR6 |
| #define VERSION_OFFSET 0x200 |
| #define VERSION_ADDRESS (USER_FLASH_FIRST_PAGE_ADDRESS + VERSION_OFFSET) |
| #define BARKER_SIZE 4 |
| #define BARKER_ADDRESS (USER_FLASH_FIRST_FILE_ADDRESS - BARKER_SIZE) |
| #define BARKER_NUMBER 0xACEC0DE |
| /* The MAX_FILE_SIZE is the size of sectors 1-5 where the firmware code |
| * will reside (minus the barker size). |
| */ |
| #define MAX_FILE_SIZE (USER_FLASH_FIRST_FILE_ADDRESS \ |
| - USER_FLASH_FIRST_PAGE_ADDRESS \ |
| - BARKER_SIZE) /* bytes */ |
| #define MAX_TRANSFER_SIZE 1024 /* bytes */ |
| #define MAX_RETRIES 5 |
| #define OPC_READ (uint8_t)(0x03) |
| #define OPC_WREN (uint8_t)(0x06) |
| #define OPC_ERPG (uint8_t)(0x20) |
| #define OPC_ERUSM (uint8_t)(0x60) |
| #define OPC_USRCD (uint8_t)(0x77) |
| |
| /* -------------- Local Data Structures ------------- */ |
| #define NUM_FLASH_TO_ERASE 5 |
| int flash_address[NUM_FLASH_TO_ERASE] = { |
| M4_FLASH_SECTOR1, |
| M4_FLASH_SECTOR2, |
| M4_FLASH_SECTOR3, |
| M4_FLASH_SECTOR4, |
| M4_FLASH_SECTOR5 |
| }; |
| int flash_delay[NUM_FLASH_TO_ERASE] = { |
| 440, 440, 440, 1320, 4000 |
| }; |
| |
| /* -------------- Global Functions ----------------- */ |
| |
| /* m4sensorhub_load_firmware() |
| |
| Check firmware and load if different from what's already on the M4. |
| Then jump to user code on M4. |
| |
| Returns 0 on success or negative error code on failure |
| |
| m4sensorhub - pointer to the main m4sensorhub data struct |
| */ |
| |
| int m4sensorhub_load_firmware(struct m4sensorhub_data *m4sensorhub, |
| unsigned short force_upgrade, |
| const struct firmware *fm) |
| { |
| const struct firmware *firmware = fm; |
| int i = MAX_RETRIES; |
| int ret = 0; |
| int bytes_left, bytes_to_write; |
| int address_to_write; |
| u8 *buf_to_read, *buf = NULL; |
| u16 fw_version_file, fw_version_device; |
| u32 barker_read_from_device; |
| int j = 0; |
| |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| ret = -ENODATA; |
| goto done; |
| } |
| |
| m4sensorhub_hw_reset(m4sensorhub); |
| |
| buf = kzalloc(MAX_TRANSFER_SIZE+8, 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: request_firmware failed for %s\n", |
| __func__, m4sensorhub->filename); |
| KDEBUG(M4SH_ERROR, "Trying to run firmware already on hw.\n"); |
| ret = 0; |
| goto done; |
| } |
| KDEBUG(M4SH_INFO, "%s: Firmware = %s\n", |
| __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); |
| |
| if (!force_upgrade) { |
| /* Verify Barker number from device */ |
| buf[0] = OPC_READ; |
| buf[1] = (BARKER_ADDRESS >> 24) & 0xFF; |
| buf[2] = (BARKER_ADDRESS >> 16) & 0xFF; |
| buf[3] = (BARKER_ADDRESS >> 8) & 0xFF; |
| buf[4] = BARKER_ADDRESS & 0xFF; |
| buf[5] = 0x00; |
| buf[6] = 0x04; |
| if (m4sensorhub_i2c_write_read(m4sensorhub, |
| buf, 7, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| msleep(100); |
| |
| if (m4sensorhub_i2c_write_read(m4sensorhub, |
| (u8 *) &barker_read_from_device, 0, BARKER_SIZE) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (barker_read_from_device != BARKER_NUMBER) { |
| KDEBUG(M4SH_NOTICE, |
| "Barker Number read 0x%8x does not match 0x%8x\n", |
| barker_read_from_device, BARKER_NUMBER); |
| KDEBUG(M4SH_NOTICE, |
| "forcing firmware update from file\n"); |
| } else { |
| |
| /* Read firmware version from device */ |
| buf[0] = OPC_READ; |
| buf[1] = (VERSION_ADDRESS >> 24) & 0xFF; |
| buf[2] = (VERSION_ADDRESS >> 16) & 0xFF; |
| buf[3] = (VERSION_ADDRESS >> 8) & 0xFF; |
| buf[4] = VERSION_ADDRESS & 0xFF; |
| buf[5] = 0x00; |
| buf[6] = 0x02; |
| if (m4sensorhub_i2c_write_read( |
| m4sensorhub, buf, 7, 0) < 0) { |
| KDEBUG(M4SH_ERROR, |
| "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| msleep(100); |
| |
| if (m4sensorhub_i2c_write_read(m4sensorhub, |
| (u8 *) &fw_version_device, 0, 2) < 0) { |
| KDEBUG(M4SH_ERROR, |
| "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| 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"); |
| 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, "Version of firmware on file is 0x%04x, " |
| "updating...\n", |
| fw_version_file); |
| } |
| |
| /* The flash memory to update has to be erased before updating */ |
| for (j = 0; j < NUM_FLASH_TO_ERASE; j++) { |
| buf[0] = OPC_ERPG; |
| buf[1] = (flash_address[j] >> 24) & 0xFF; |
| buf[2] = (flash_address[j] >> 16) & 0xFF; |
| buf[3] = (flash_address[j] >> 8) & 0xFF; |
| buf[4] = flash_address[j] & 0xFF; |
| buf[5] = 0x00; |
| buf[6] = 0x01; |
| if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 7, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| KDEBUG(M4SH_ERROR, "Erasing %8x address failed\n", |
| flash_address[j]); |
| ret = -EINVAL; |
| goto done; |
| } |
| msleep(flash_delay[j]); |
| } |
| |
| 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__, |
| firmware->size); |
| |
| while (bytes_left && i) { |
| if (bytes_left > MAX_TRANSFER_SIZE) |
| bytes_to_write = MAX_TRANSFER_SIZE; |
| else |
| bytes_to_write = bytes_left; |
| |
| buf[0] = OPC_WREN; |
| buf[1] = (address_to_write >> 24) & 0xFF; |
| buf[2] = (address_to_write >> 16) & 0xFF; |
| buf[3] = (address_to_write >> 8) & 0xFF; |
| buf[4] = address_to_write & 0xFF; |
| buf[5] = (bytes_to_write >> 8) & 0xFF; |
| buf[6] = bytes_to_write & 0xFF; |
| buf[7] = 0xFF; |
| memcpy(&buf[8], buf_to_read, bytes_to_write); |
| if (m4sensorhub_i2c_write_read(m4sensorhub, buf, |
| bytes_to_write+8, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| msleep(20); |
| |
| /* Read back the code that was written and validate it */ |
| buf[0] = OPC_READ; |
| buf[1] = (address_to_write >> 24) & 0xFF; |
| buf[2] = (address_to_write >> 16) & 0xFF; |
| buf[3] = (address_to_write >> 8) & 0xFF; |
| buf[4] = address_to_write & 0xFF; |
| buf[5] = (bytes_to_write >> 8) & 0xFF; |
| buf[6] = bytes_to_write & 0xFF; |
| if (m4sensorhub_i2c_write_read( |
| m4sensorhub, buf, 7, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 0, |
| bytes_to_write) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (memcmp(buf, buf_to_read, bytes_to_write) != 0) { |
| /* If memory write fails, try again */ |
| KDEBUG(M4SH_ERROR, |
| "memory write to 0x%x of %d bytes failed, try again\n", |
| address_to_write, bytes_to_write); |
| i--; |
| } else { |
| address_to_write += bytes_to_write; |
| buf_to_read += bytes_to_write; |
| bytes_left -= bytes_to_write; |
| /* Reset reter of retries */ |
| i = MAX_RETRIES; |
| } |
| } |
| |
| if (!i) { |
| KDEBUG(M4SH_ERROR, "%s: firmware transfer failed\n", __func__); |
| ret = -EINVAL; |
| } else { |
| /* Write barker number when firmware successfully written */ |
| buf[0] = OPC_WREN; |
| buf[1] = (BARKER_ADDRESS >> 24) & 0xFF; |
| buf[2] = (BARKER_ADDRESS >> 16) & 0xFF; |
| buf[3] = (BARKER_ADDRESS >> 8) & 0xFF; |
| buf[4] = BARKER_ADDRESS & 0xFF; |
| buf[5] = 0x00; |
| buf[6] = 0x04; |
| buf[7] = 0xFF; |
| buf[8] = BARKER_NUMBER & 0xFF; |
| buf[9] = (BARKER_NUMBER >> 8) & 0xFF; |
| buf[10] = (BARKER_NUMBER >> 16) & 0xFF; |
| buf[11] = (BARKER_NUMBER >> 24) & 0xFF; |
| if (m4sensorhub_i2c_write_read(m4sensorhub, |
| buf, 12, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| KDEBUG(M4SH_NOTICE, "%s: %d bytes written successfully\n", |
| __func__, firmware->size); |
| } |
| |
| done: |
| release_firmware(firmware); |
| |
| /* If ret is invalid, then we don't try to jump to user code */ |
| if (ret >= 0 && m4sensorhub_jump_to_user(m4sensorhub) < 0) |
| /* If jump to user code fails, return failure */ |
| ret = -EINVAL; |
| |
| kfree(buf); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(m4sensorhub_load_firmware); |
| |
| |
| /* -------------- Local Functions ----------------- */ |
| |
| static int m4sensorhub_jump_to_user(struct m4sensorhub_data *m4sensorhub) |
| { |
| int ret = -1; |
| u8 buf[7] = {0}; |
| u32 barker_read_from_device = 0; |
| |
| buf[0] = OPC_READ; |
| buf[1] = (BARKER_ADDRESS >> 24) & 0xFF; |
| buf[2] = (BARKER_ADDRESS >> 16) & 0xFF; |
| buf[3] = (BARKER_ADDRESS >> 8) & 0xFF; |
| buf[4] = BARKER_ADDRESS & 0xFF; |
| buf[5] = 0x00; |
| buf[6] = 0x04; |
| if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 7, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| return ret; |
| } |
| |
| msleep(100); |
| |
| if (m4sensorhub_i2c_write_read(m4sensorhub, |
| (u8 *) &barker_read_from_device, 0, BARKER_SIZE) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| return ret; |
| } |
| |
| if (barker_read_from_device == BARKER_NUMBER) { |
| buf[0] = OPC_USRCD; |
| if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 1, 0) < 0) { |
| KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n", |
| __func__, __LINE__); |
| return ret; |
| } |
| KDEBUG(M4SH_NOTICE, "Executing M4 code\n"); |
| msleep(5000); /* 5 secs delay */ |
| ret = 0; |
| } else { |
| KDEBUG(M4SH_ERROR, |
| "Barker Number read 0x%8x does not match 0x%8x\n", |
| barker_read_from_device, BARKER_NUMBER); |
| KDEBUG(M4SH_ERROR, "*** Not executing M4 code ***\n"); |
| } |
| |
| return ret; |
| } |