Пример #1
0
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};
	ssp_dbgf();
	cmd.cmd = EXT_ER_COMMAND;
	cmd.xor_cmd = XOR_EXT_ER_COMMAND;
	cmd.timeout = DEF_ACKCMD_NUMBER;
	cmd.ack_pad = BL_DUMMY;

	ret = stm32fwu_spi_send_cmd(spi, &cmd);

	if (ret != BL_ACK) {
		ssp_err("fw_erase failed - %d", ret);
		return ret;
	}

	ret = stm32fwu_spi_write(spi, buff, EXT_ER_DATA_LEN);
	if (ret < EXT_ER_DATA_LEN) {
		ssp_err("fw_erase write failed");
		return 0;
	}

	dummy_cmd.timeout = DEF_ACK_ERASE_NUMBER;

	ret = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
	if (ret == BL_ACK)
		return 0;
	else if (ret == BL_NACK)
		return -EPROTO;
	else
		return -ETIME;
}
Пример #2
0
static int update_mcu_bin(struct ssp_data *data, int iBinType)
{
	int retry = BLMODE_RETRYCOUNT;
	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);

	/* 1. Start system boot mode */
	do {
		iRet = change_to_bootmode(data);
		ssp_info("bootmode %d retry: %d", iRet, 3 - retry);
	} while (retry-- > 0 && iRet != BL_ACK);

	if (iRet != BL_ACK) {
		ssp_errf("change_to_bootmode %d", iRet);
		return iRet;
	}

	/* 2. Flash erase all */
	iRet = fw_erase_stm(data->spi);
	if (iRet < 0) {
		ssp_errf("fw_erase_stm %d", iRet);
		return iRet;
	}

	switch (iBinType) {
	case KERNEL_BINARY:
	/* HW request: I2C line is reversed */
		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:
		ssp_err("binary type error!!");
	}

	/* STM : GO USER ADDR */
	stm32fwu_spi_send_cmd(data->spi, &cmd);
	send_addr(data->spi, STM_APP_ADDR, 0);

	data->spi->mode = SPI_MODE_1;
	if (spi_setup(data->spi))
		ssp_err("failed to setup spi mode for app");
	usleep_range(1000, 1100);

	return iRet;
}
Пример #3
0
int check_fwbl(struct ssp_data *data)
{

	unsigned int fw_revision;

	fw_revision = SSP_FIRMWARE_REVISION_STM;

	data->uCurFirmRev = get_firmware_rev(data);

	if ((data->uCurFirmRev == SSP_INVALID_REVISION)
			|| (data->uCurFirmRev == SSP_INVALID_REVISION2)) {
		data->uCurFirmRev = SSP_INVALID_REVISION;
		ssp_err("SSP_INVALID_REVISION");
		return FW_DL_STATE_NEED_TO_SCHEDULE;
	} else {
		if (data->uCurFirmRev != fw_revision) {
			ssp_info("MCU Firm Rev : Old = %8u, New = %8u",
				data->uCurFirmRev, fw_revision);

			return FW_DL_STATE_NEED_TO_SCHEDULE;
		}
		ssp_info("MCU Firm Rev : Old = %8u, New = %8u",
			data->uCurFirmRev, fw_revision);
	}

	return FW_DL_STATE_NONE;
}
Пример #4
0
int forced_to_download_binary(struct ssp_data *data, int iBinType)
{
	int iRet = 0;
	int retry = 3;

	ssp_infof("mcu binany update!");

	ssp_enable(data, false);

	data->fw_dl_state = FW_DL_STATE_DOWNLOADING;
	ssp_infof("DL state = %d", data->fw_dl_state);
	data->spi->max_speed_hz = BOOT_SPI_HZ;
	if (spi_setup(data->spi))
		ssp_err("failed to setup spi for ssp_boot");
	do {
		ssp_info("%d try", 3 - retry);
		iRet = update_mcu_bin(data, iBinType);
	} while (retry-- > 0 && iRet < 0);

	data->spi->max_speed_hz = NORM_SPI_HZ;

	if (spi_setup(data->spi))
		ssp_err("failed to setup spi for ssp_norm");
	if (iRet < 0) {
		ssp_infof("update_mcu_bin failed!");
		goto out;
	}

	data->fw_dl_state = FW_DL_STATE_SYNC;
	ssp_infof("DL state = %d", data->fw_dl_state);
	ssp_enable(data, true);

	get_proximity_threshold(data);
	proximity_open_calibration(data);
	accel_open_calibration(data);
	gyro_open_calibration(data);
	pressure_open_calibration(data);

	data->fw_dl_state = FW_DL_STATE_DONE;
	ssp_infof("DL state = %d", data->fw_dl_state);

	iRet = SUCCESS;
out:
	return iRet;
}
Пример #5
0
static int change_to_bootmode(struct ssp_data *data)
{
	int iCnt;
	int ret;
	char syncb = BL_SPI_SOF;
	int ncount = 5;
	struct stm32fwu_spi_cmd dummy_cmd;
	ssp_dbgf();

	/* dummy_cmd.timeout = DEF_ACKCMD_NUMBER; */
	dummy_cmd.timeout = ncount;

	gpio_set_value_cansleep(data->rst, 0);
	usleep_range(4000, 4400);
	gpio_set_value_cansleep(data->rst, 1);
	usleep_range(45000, 47000);

	for (iCnt = 0; iCnt < 9; iCnt++) {
		gpio_set_value_cansleep(data->rst, 0);
		usleep_range(4000, 4400);
		gpio_set_value_cansleep(data->rst, 1);
		usleep_range(15000, 15500);
	}

	data->spi->mode = SPI_MODE_0;
	if (spi_setup(data->spi))
		ssp_err("failed to setup spi mode for boot");
	usleep_range(1000, 1100);

	msleep(30);

	while (ncount-- >= 0) {
		ret = stm32fwu_spi_write(data->spi, &syncb, 1);
#if SSP_STM_DEBUG
		ssp_info("stm32fwu_spi_write(sync byte) returned %d", ret);
#endif
		ret = stm32fwu_spi_wait_for_ack(data->spi, &dummy_cmd, BL_DUMMY);
#if SSP_STM_DEBUG
		ssp_info("stm32fwu_spi_wait_for_ack returned %d (0x%x)", ret, ret);
#endif
		if (ret == BL_ACK)
			break;
	}

	return ret;
}
Пример #6
0
static int ssp_parse_dt(struct device *dev,
	struct ssp_data *data)
{
	struct device_node *np = dev->of_node;
	enum of_gpio_flags flags;
	int errorno = 0;

	/* gpio pins */
	data->mcu_int1 = of_get_named_gpio_flags(np, "ssp,mcu_int1-gpio",
		0, &flags);
	if (data->mcu_int1 < 0) {
		errorno = data->mcu_int1;
		goto dt_exit;
	}

	data->mcu_int2 = of_get_named_gpio_flags(np, "ssp,mcu_int2-gpio",
		0, &flags);
	if (data->mcu_int2 < 0) {
		errorno = data->mcu_int2;
		goto dt_exit;
	}

	data->ap_int = of_get_named_gpio_flags(np, "ssp,ap_int-gpio",
		0, &flags);
	if (data->ap_int < 0) {
		errorno = data->ap_int;
		goto dt_exit;
	}

	data->rst = of_get_named_gpio_flags(np, "ssp,rst-gpio",
		0, &flags);
	if (data->rst < 0) {
		errorno = data->rst;
		goto dt_exit;
	}

	/* sensor positions */
	if (of_property_read_u32(np, "ssp,acc-position", &data->accel_position))
		data->accel_position = 0;

	if (of_property_read_u32(np, "ssp,mag-position", &data->mag_position))
		data->mag_position = 0;

	ssp_info("acc-posi[%d] mag-posi[%d]",
			data->accel_position, data->mag_position);

	/* prox thresh */
	if (of_property_read_u32(np, "ssp,prox-hi_thresh",
			&data->uProxHiThresh_default))
		data->uProxHiThresh_default = DEFUALT_HIGH_THRESHOLD;

	if (of_property_read_u32(np, "ssp,prox-low_thresh",
			&data->uProxLoThresh_default))
		data->uProxLoThresh_default = DEFUALT_LOW_THRESHOLD;

	ssp_info("hi-thresh[%u] low-thresh[%u]",
		data->uProxHiThresh_default, data->uProxLoThresh_default);

#ifdef CONFIG_SENSORS_MULTIPLE_GLASS_TYPE
    	if (of_property_read_u32(np, "ssp-glass-type", &data->glass_type))
		    data->glass_type = 0;
#endif

	/* mag matrix */
	if (of_property_read_u8_array(np, "ssp,mag-array",
		data->pdc_matrix, sizeof(data->pdc_matrix))) {
		ssp_err("no mag-array, set as 0");
	}

	/* set off gpio pins */
	errorno = gpio_request(data->mcu_int1, "mcu_ap_int1");
	if (errorno) {
		ssp_err("failed to request MCU_INT1 for SSP");
		goto dt_exit;
	}
	errorno = gpio_direction_input(data->mcu_int1);
	if (errorno) {
		ssp_err("failed to set mcu_int1 as input");
		goto dt_exit;
	}
	errorno = gpio_request(data->mcu_int2, "MCU_INT2");
	if (errorno) {
		ssp_err("failed to request MCU_INT2 for SSP");
		goto dt_exit;
	}
	gpio_direction_input(data->mcu_int2);

	errorno = gpio_request(data->ap_int, "AP_MCU_INT");
	if (errorno) {
		ssp_err("failed to request AP_INT for SSP");
		goto dt_exit;
	}
	gpio_direction_output(data->ap_int, 1);

	errorno = gpio_request(data->rst, "MCU_RST");
	if (errorno) {
		ssp_err("failed to request MCU_RST for SSP");
		goto dt_exit;
	}
	gpio_direction_output(data->rst, 1);

dt_exit:
	return errorno;
}
Пример #7
0
int debug_crash_dump(struct ssp_data *data, char *pchRcvDataFrame, int iLength)
{
	struct timeval cur_time;
	char strFilePath[100];
	int iRetWrite = 0;
	unsigned char datacount = pchRcvDataFrame[1];
	unsigned int databodysize = iLength - 2;
	char *databody = &pchRcvDataFrame[2];
/*
	if(iLength != DEBUG_DUMP_DATA_SIZE)
	{
		ssp_errf("data length error(%d)", iLength);
		return FAIL;
	}
	else
		ssp_errf("length(%d)", databodysize);
*/
	ssp_errf("length(%d)", databodysize);

	if (data->bSspShutdown) {
		ssp_infof("ssp shutdown, stop dumping");
		return FAIL;
	}

	if (data->bMcuDumpMode == true)	{
		wake_lock(&data->ssp_wake_lock);

		if (data->realtime_dump_file == NULL) {
			backup_fs = get_fs();
			set_fs(get_ds());

			do_gettimeofday(&cur_time);

			snprintf(strFilePath, sizeof(strFilePath), "%s%d.dump",
				DEBUG_DUMP_FILE_PATH, (int)cur_time.tv_sec);
			data->realtime_dump_file = filp_open(strFilePath,
					O_RDWR | O_CREAT | O_APPEND, 0660);

			ssp_err("save_crash_dump : open file(%s)", strFilePath);

			if (IS_ERR(data->realtime_dump_file)) {
				ssp_errf("Can't open dump file");
				set_fs(backup_fs);
				data->realtime_dump_file = NULL;
				wake_unlock(&data->ssp_wake_lock);
				return FAIL;
			}
		}

		data->total_dump_size += databodysize;
		/* ssp_errf("total receive size(%d)", data->total_dump_size); */
		iRetWrite = vfs_write(data->realtime_dump_file,
					(char __user *)databody, databodysize,
					&data->realtime_dump_file->f_pos);
		if (iRetWrite < 0) {
			ssp_errf("Can't write dump to file");
			wake_unlock(&data->ssp_wake_lock);
			return FAIL;
		}

		if (datacount == DEBUG_DUMP_DATA_COMPLETE) {
			ssp_errf("close file(size=%d)", data->total_dump_size);
			filp_close(data->realtime_dump_file, current->files);
			set_fs(backup_fs);
			data->uDumpCnt++;
			data->total_dump_size = 0;
			data->realtime_dump_file = NULL;
			data->bDumping = false;
		}

		wake_unlock(&data->ssp_wake_lock);

		/*
		if(iLength == 2*1024)
			queue_refresh_task(data, 0);
		*/
	}

	return SUCCESS;
}
Пример #8
0
static int stm32fwu_spi_wait_for_ack(struct spi_device *spi,
				struct stm32fwu_spi_cmd *cmd, u8 dummy_bytes)
{
	static int check_spi_wait_cnt = 1;

	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;
	dummy_bytes = BL_DUMMY;
#if SSP_STM_DEBUG
	ssp_infof("dummy byte = 0x%02hhx", 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 (ret < 0) {
			dev_err(&spi->dev, "%s: spi error %d\n", __func__, ret);
			return ret;
		} else if ((rx_buf == BL_ACK) || (rx_buf == BL_NACK)) {
			/* ACK cmd set */
			stm32fwu_spi_send_ack(spi, BL_ACK);
			return (int)rx_buf;
		} else {
			/* Cross cmd set */
			tx_buf = rx_buf;
		}
		if (check_spi_wait_cnt % 20 == 0)
			usleep_range(1000, 1100);
		else
			usleep_range(1000, 1100);
		i++;
		check_spi_wait_cnt++;
	}
#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;
	int ret;
#if BYTETOBYTE_USED
	int i;
	struct spi_transfer t[STM_MAX_BUFFER_SIZE];
	memset(t, 0, STM_MAX_BUFFER_SIZE * sizeof(struct spi_transfer));
#else
	struct spi_transfer	t = {
		.tx_buf		= tx_buf,
		.rx_buf		= rx_buf,
		.len		= 3,
		.bits_per_word = 8,
	};
#endif
	ssp_dbgf();

	spi_message_init(&m);
	tx_buf[0] = BL_SPI_SOF;
	tx_buf[1] = cmd->cmd;
	tx_buf[2] = cmd->xor_cmd;

#if BYTETOBYTE_USED
	for (i = 0; i < 3; i++) {
		t[i].tx_buf = &tx_buf[i];
		t[i].rx_buf = &rx_buf[i];
		t[i].len = 1;
		t[i].bits_per_word = 8;
		t[i].delay_usecs = BYTE_DELAY_WRITE;
		spi_message_add_tail(&t[i], &m);
	}
#else
	spi_message_add_tail(&t, &m);
#endif

	ret = spi_sync(spi, &m);
	if (ret < 0) {
		dev_err(&spi->dev, "%s: spi error %d\n", __func__, ret);
		return ret;
	}

	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) {
		ssp_errf("Got NAK or Error %d", ret);
		return ret;
	}

	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;
#if BYTETOBYTE_USED
	struct spi_transfer t[STM_MAX_BUFFER_SIZE];
	memset(t, 0, STM_MAX_BUFFER_SIZE * sizeof(struct spi_transfer));
	int i;
#else
	struct spi_transfer	t = {
		.tx_buf		= buffer,
		.rx_buf		= rx_buf,
		.len		= (unsigned int)len,
		.bits_per_word = 8,
	};
#endif
	spi_message_init(&m);
#if BYTETOBYTE_USED
	for (i = 0; i < len; i++) {
		t[i].tx_buf = &buffer[i];
		t[i].rx_buf = &rx_buf[i];
		t[i].len = 1;
		t[i].bits_per_word = 8;
		t[i].delay_usecs = BYTE_DELAY_WRITE;
		spi_message_add_tail(&t[i], &m);
	}
#else
	spi_message_add_tail(&t, &m);
#endif
	ret = spi_sync(spi, &m);

	if (ret < 0) {
		ssp_err("Error in %d spi_write()", 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 = DEF_ACKROOF_NUMBER;
	ssp_dbgf();

	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];

	res = stm32fwu_spi_write(spi, &header[i], len);

	if (res < len) {
		ssp_err("Error in sending address. Res %d", res);
		return (res > 0) ? -EIO : res;
	}

	res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
	if (res != BL_ACK) {
		ssp_err("send_addr(): rcv_ack returned 0x%x", res);
		return res;
	}
	return 0;
}
Пример #9
0
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;
	int block = STM_MAX_XFER_SIZE;
	int count = 0;
	int err_count = 0;
	int retry_count = 0;

	ssp_info("ssp_load_fw start!!");

	iRet = request_firmware(&fw, pFn, &spi->dev);
	if (iRet) {
		ssp_err("Unable to open firmware %s", 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) {
				ssp_err("Err writing to addr 0x%08X", fw_addr);
				if (iRet < 0) {
					ssp_err("Error was %d", iRet);
				} else {
					ssp_err("Incomplete write of %d bytes",
						iRet);
					iRet = -EIO;
				}
				retry_count++;
				err_count++;
			} else {
				retry_count = 0;
				break;
			}
		}
		if (iRet < 0) {
			ssp_err("Writing MEM failed: %d, retry cont: %d",
				iRet, err_count);
			goto out_load_kernel;
		}

		remaining -= block;
		uPos += block;
		fw_addr += block;
		if (count++ == 20) {
			ssp_info("Updated %u bytes / %u bytes", uPos,
				(unsigned int)fw->size);
			count = 0;
		}
	}

	ssp_info("Firmware download is success(%d bytes, retry %d)",
		uPos, err_count);

out_load_kernel:
	release_firmware(fw);
	return iRet;
}
Пример #10
0
static int load_ums_fw_bootmode(struct spi_device *spi, const char *pFn)
{
	const u8 *buff = NULL;
	char fw_path[BL_UMS_FW_PATH+1];
	unsigned int uFSize = 0, uNRead = 0;
	unsigned int uPos = 0;
	int iRet = SUCCESS;
	int remaining;
	int block = STM_MAX_XFER_SIZE;
	unsigned int fw_addr = STM_APP_ADDR;
	int retry_count = 0;
	int err_count = 0;
	int count = 0;
	struct file *fp = NULL;
	mm_segment_t old_fs = get_fs();

	ssp_info("ssp_load_ums_fw start!!");

	old_fs = get_fs();
	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;
		ssp_err("file %s open error", fw_path);
		goto err_open;
	}

	uFSize = (unsigned int)fp->f_path.dentry->d_inode->i_size;
	ssp_info("ssp_load_ums firmware size: %u", uFSize);

	buff = kzalloc((size_t)STM_MAX_XFER_SIZE, GFP_KERNEL);
	if (!buff) {
		iRet = ERROR;
		ssp_err("fail to alloc buffer for fw");
		goto err_alloc;
	}

	remaining = uFSize;

	while (remaining > 0) {
		if (block > remaining)
			block = remaining;

		uNRead = (unsigned int)vfs_read(fp, (char __user *)buff,
				(unsigned int)block, &fp->f_pos);
		if (uNRead != block) {
			iRet = ERROR;
			ssp_err("fail to read file %s (nread = %u)", fw_path, uNRead);
			goto err_fw_size;
		}
		while (retry_count < 3) {
			iRet = fw_write_stm(spi, fw_addr, block, buff);
			if (iRet < block) {
				ssp_err("Err writing to addr 0x%08X", fw_addr);
				if (iRet < 0) {
					ssp_err("Error was %d", iRet);
				} else {
					ssp_err("Incomplete write of %d bytes",
						iRet);
					iRet = -EIO;
				}
				retry_count++;
				err_count++;
			} else {
				retry_count = 0;
				break;
			}
		}
		if (iRet < 0) {
			ssp_err("Writing MEM failed: %d, retry cont: %d",
				iRet, err_count);
			goto out;
		}
		remaining -= block;
		uPos += block;
		fw_addr += block;
		if (count++ == 50) {
			ssp_info("Updated %u bytes / %u bytes", uPos, uFSize);
			count = 0;
		}
	}

	ssp_info("Firm up(UMS) success(%d bytes, retry %d)", uPos, err_count);
out:
err_fw_size:
	kfree(buff);
err_alloc:
	filp_close(fp, NULL);
err_open:
	set_fs(old_fs);

	return iRet;
}
Пример #11
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 = DEF_ACKCMD_NUMBER;
	cmd.ack_pad = (u8)((fw_addr >> 24) & 0xFF);
	ssp_dbgf();
#if SSP_STM_DEBUG
	ssp_info("sending WMEM_COMMAND");
#endif

	if (len > STM_MAX_XFER_SIZE) {
		ssp_err("Can't send more than 256 bytes per transaction");
		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) {
		ssp_err("Error %d sending read_mem cmd", res);
		return res;
	}

	res = send_addr(spi, fw_addr, 0);

	if (res != 0) {
		ssp_err("Error %d sending write_mem Address", res);
		return res;
	}

	res = stm32fwu_spi_write(spi, send_buff, len + 2);
	if (res < len) {
		ssp_err("Error writing to flash. res = %d", res);
		return (res > 0) ? -EIO : res;
	}
	ssp_dbgf("2");

	dummy_cmd.timeout = DEF_ACKROOF_NUMBER;
	usleep_range(100, 150); /* Samsung added */
	res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
	if (res == BL_ACK)
		return len;

	if (res == BL_NACK) {
		ssp_err("Got NAK waiting for WRITE_MEM to complete");
		return -EPROTO;
	}
	ssp_err("timeout waiting for ACK for WRITE_MEM command");
	return -ETIME;
}