// SPDX-License-Identifier: GPL-2.0-or-later

/* To be built with arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m0 -O3 bluenrgx.c */
/* Then postprocess output of command "arm-none-eabi-objdump -d bluenrgx.o" to make a C array of bytes */

#include <stdint.h>
#include "../../../../src/flash/nor/bluenrg-x.h"

/* Status Values ----------------------------------------------------------*/
#define SUCCESS             0
#define ERR_UNALIGNED       1
#define ERR_INVALID_ADDRESS 2
#define ERR_INVALID_TYPE    3
#define ERR_WRITE_PROTECTED 4
#define ERR_WRITE_FAILED    5
#define ERR_ERASE_REQUIRED  6
#define ERR_VERIFY_FAILED   7

#define MFB_MASS_ERASE 0x01
#define MFB_PAGE_ERASE 0x02

#define DO_ERASE  0x0100
#define DO_VERIFY 0x0200

#define MFB_BOTTOM                (0x10040000)
#define MFB_SIZE_B(regs_base)     ((16 * (((*(volatile uint32_t *)(regs_base + FLASH_SIZE_REG)) + 1) >> 12)) * 1024)
#define MFB_SIZE_W                (MFB_SIZE_B/4)
#define MFB_TOP                   (MFB_BOTTOM+MFB_SIZE_B-1)
#define MFB_PAGE_SIZE_B           (2048)
#define MFB_PAGE_SIZE_W           (MFB_PAGE_SIZE_B/4)

#define AREA_ERROR 0x01
#define AREA_MFB   0x04

typedef struct {
	volatile uint8_t *wp;
	uint8_t *rp;
} work_area_t;

/* Flash Commands --------------------------------------------------------*/
static inline __attribute__((always_inline)) uint32_t flashWrite(uint32_t address, uint8_t **data,
								 uint32_t writeLength, uint32_t flash_regs_base)
{
	uint32_t index, flash_word[4];
	uint8_t i;

	*((volatile uint32_t *)(flash_regs_base + FLASH_REG_IRQMASK)) = 0;
	for (index = 0; index < writeLength; index += (FLASH_WORD_LEN*4)) {
		for (i = 0; i < 4; i++)
			flash_word[i] = (*(uint32_t *) (*data + i*4));

		/* Clear the IRQ flags */
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_IRQRAW)) = 0x0000003F;
		/* Load the flash address to write */
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_ADDRESS)) = (uint16_t)((address + index - MFB_BOTTOM) >> 2);
		/* Prepare and load the data to flash */
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_DATA0)) = flash_word[0];
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_DATA1)) = flash_word[1];
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_DATA2)) = flash_word[2];
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_DATA3)) = flash_word[3];
		/* Flash write command */
		*((volatile uint32_t *)(flash_regs_base + FLASH_REG_COMMAND)) = FLASH_CMD_BURSTWRITE;
		/* Wait the end of the flash write command */
		while ((*((volatile uint32_t *)(flash_regs_base + FLASH_REG_IRQRAW)) & FLASH_INT_CMDDONE) == 0)
			;
		*data += (FLASH_WORD_LEN * 4);
	}

	return SUCCESS;
}

__attribute__((naked)) __attribute__((noreturn)) void write(uint8_t *work_area_p,
							    uint8_t *fifo_end,
							    uint8_t *target_address,
							    uint32_t count,
							    uint32_t flash_regs_base)
{
	uint32_t retval;
	volatile work_area_t *work_area = (work_area_t *) work_area_p;
	uint8_t *fifo_start = (uint8_t *) work_area->rp;

	while (count) {
		volatile int32_t fifo_linear_size;

		/* Wait for some data in the FIFO */
		while (work_area->rp == work_area->wp)
			;
		if (work_area->wp == 0) {
			/* Aborted by other party */
			break;
		}
		if (work_area->rp > work_area->wp) {
			fifo_linear_size = fifo_end-work_area->rp;
		} else {
			fifo_linear_size = (work_area->wp - work_area->rp);
			if (fifo_linear_size < 0)
				fifo_linear_size = 0;
		}
		if (fifo_linear_size < 16) {
			/* We should never get here */
			continue;
		}

		retval = flashWrite((uint32_t) target_address, (uint8_t **) &work_area->rp, fifo_linear_size, flash_regs_base);
		if (retval != SUCCESS) {
			work_area->rp = (uint8_t *)retval;
			break;
		}
		target_address += fifo_linear_size;
		if (work_area->rp >= fifo_end)
			work_area->rp = fifo_start;
		count -= fifo_linear_size;
	}
	__asm("bkpt 0");
}
