diff options
| author | Doug Zobel <dzobel1@motorola.com> | 2013-11-15 14:29:07 -0600 |
|---|---|---|
| committer | James Wylder <jwylder@motorola.com> | 2014-03-05 17:46:52 -0600 |
| commit | d2a782003a6047da120a33e6f8ee6fd33bb825d6 (patch) | |
| tree | 8d20bd4ecda62a06e98993c4108456bc1acb0d0b /drivers/mfd/m4sensorhub-stm32-fw.c | |
| parent | 32fd2d36d2464056d4522a9c02797b7c2b2e884f (diff) | |
| download | olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.tar.xz olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.zip | |
CW integration and minnow bringup
* create minnow machine type
* create Android makefile
* add pre-commit syntax check
* enable -Werror
* Add drivers: CPCAP, TPS65xxx, m4sensorhub, atmxt, lm3535,
usb gadget, minnow display, TI 12xx wireless
Change-Id: I7962f5e1256715f2452aed5a62a4f2f2383d5046
Diffstat (limited to 'drivers/mfd/m4sensorhub-stm32-fw.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-stm32-fw.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-stm32-fw.c b/drivers/mfd/m4sensorhub-stm32-fw.c new file mode 100644 index 00000000000..55aca3ebb05 --- /dev/null +++ b/drivers/mfd/m4sensorhub-stm32-fw.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2012 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 -------------- */ +/* Firmware */ +#define FIRMWARE_NAME "m4sensorhub.bin" +/* 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 *firmware; + 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; + + buf = kzalloc(MAX_TRANSFER_SIZE+8, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto done; + } + + ret = request_firmware(&firmware, FIRMWARE_NAME, + &m4sensorhub->i2c_client->dev); + if (ret < 0) { + KDEBUG(M4SH_ERROR, "%s: request_firmware failed for %s\n", + __func__, FIRMWARE_NAME); + KDEBUG(M4SH_ERROR, "Trying to run firmware already on hw.\n"); + ret = 0; + goto done; + } + + 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\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; +} |