/* Recreated Espressif libmain os_cpu_o contents.

   Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
   BSD Licensed as described in the file LICENSE
*/
#include "open_esplibs.h"
#if OPEN_LIBMAIN_SPI_FLASH
// The contents of this file are only built if OPEN_LIBMAIN_SPI_FLASH is set to true

#include "FreeRTOS.h"
#include "common_macros.h"
#include "esp/spi_regs.h"
#include "esp/rom.h"
#include "sdk_internal.h"
#include "espressif/spi_flash.h"

sdk_flashchip_t sdk_flashchip = {
    0x001640ef,      // device_id
    4 * 1024 * 1024, // chip_size
    65536,           // block_size
    4096,            // sector_size
    256,             // page_size
    0x0000ffff,      // status_mask
};

// NOTE: This routine appears to be completely unused in the SDK

int IRAM sdk_SPIReadModeCnfig(uint32_t mode) {
    uint32_t ctrl_bits;

    SPI(0).CTRL0 &= ~(SPI_CTRL0_FASTRD_MODE | SPI_CTRL0_DOUT_MODE | SPI_CTRL0_QOUT_MODE | SPI_CTRL0_DIO_MODE | SPI_CTRL0_QIO_MODE);
    if (mode == 0) {
        ctrl_bits = SPI_CTRL0_FASTRD_MODE | SPI_CTRL0_QIO_MODE;
    } else if (mode == 1) {
        ctrl_bits = SPI_CTRL0_FASTRD_MODE | SPI_CTRL0_QOUT_MODE;
    } else if (mode == 2) {
        ctrl_bits = SPI_CTRL0_FASTRD_MODE | SPI_CTRL0_DIO_MODE;
    } else if (mode == 3) {
        ctrl_bits = SPI_CTRL0_FASTRD_MODE | SPI_CTRL0_DOUT_MODE;
    } else if (mode == 4) {
        ctrl_bits = SPI_CTRL0_FASTRD_MODE;
    } else { 
        ctrl_bits = 0;
    }
    if (mode == 0 || mode == 1) {
        Enable_QMode(&sdk_flashchip);
    } else {
        Disable_QMode(&sdk_flashchip);
    }
    SPI(0).CTRL0 |= ctrl_bits;
    return 0;
}

sdk_SpiFlashOpResult IRAM sdk_SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size) {
    uint32_t first_page_portion;
    uint32_t pos;
    uint32_t full_pages;
    uint32_t bytes_remaining;

    if (des_addr + size <= sdk_flashchip.chip_size) {
        first_page_portion = sdk_flashchip.page_size - (des_addr % sdk_flashchip.page_size);
        if (size < first_page_portion) {
            if (SPI_page_program(&sdk_flashchip, des_addr, src_addr, size)) {
                return SPI_FLASH_RESULT_ERR;
            } else {
                return SPI_FLASH_RESULT_OK;
            }
        }
    } else {
       return SPI_FLASH_RESULT_ERR;
    }
    if (SPI_page_program(&sdk_flashchip, des_addr, src_addr, first_page_portion)) {
        return SPI_FLASH_RESULT_ERR;
    }
    pos = first_page_portion;
    bytes_remaining = size - first_page_portion;
    full_pages = bytes_remaining / sdk_flashchip.page_size;
    if (full_pages) {
        for (int i = 0; i != full_pages; i++) {
            if (SPI_page_program(&sdk_flashchip, des_addr + pos, src_addr + (pos / 4), sdk_flashchip.page_size)) {
                return SPI_FLASH_RESULT_ERR;
            }
            pos += sdk_flashchip.page_size;
        }
        bytes_remaining = size - pos;
    }
    if (SPI_page_program(&sdk_flashchip, des_addr + pos, src_addr + (pos / 4), bytes_remaining)) {
        return SPI_FLASH_RESULT_ERR;
    }
    return SPI_FLASH_RESULT_OK;
}

sdk_SpiFlashOpResult IRAM sdk_SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size) {
    if (SPI_read_data(&sdk_flashchip, src_addr, des_addr, size)) {
        return SPI_FLASH_RESULT_ERR;
    } else {
        return SPI_FLASH_RESULT_OK;
    }
}

sdk_SpiFlashOpResult IRAM sdk_SPIEraseSector(uint16_t sec) {
    if (sec >= sdk_flashchip.chip_size / sdk_flashchip.sector_size) {
        return SPI_FLASH_RESULT_ERR;
    }
    if (SPI_write_enable(&sdk_flashchip)) {
        return SPI_FLASH_RESULT_ERR;
    }
    if (SPI_sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sec)) {
        return SPI_FLASH_RESULT_ERR;
    }
    return SPI_FLASH_RESULT_OK;
}

uint32_t IRAM sdk_spi_flash_get_id(void) {
    uint32_t result;

    portENTER_CRITICAL();
    Cache_Read_Disable();
    Wait_SPI_Idle(&sdk_flashchip);
    SPI(0).W[0] = 0;
    SPI(0).CMD = SPI_CMD_READ_ID;
    while (SPI(0).CMD != 0) {}
    result = SPI(0).W[0] & 0x00ffffff;
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

sdk_SpiFlashOpResult IRAM sdk_spi_flash_read_status(uint32_t *status) {
    sdk_SpiFlashOpResult result;

    portENTER_CRITICAL();
    Cache_Read_Disable();
    result = SPI_read_status(&sdk_flashchip, status);
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

sdk_SpiFlashOpResult IRAM sdk_spi_flash_write_status(uint32_t status) {
    sdk_SpiFlashOpResult result;

    portENTER_CRITICAL();
    Cache_Read_Disable();
    result = SPI_write_status(&sdk_flashchip, status);
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

sdk_SpiFlashOpResult IRAM sdk_spi_flash_erase_sector(uint16_t sec) {
    sdk_SpiFlashOpResult result;

    portENTER_CRITICAL();
    Cache_Read_Disable();
    result = sdk_SPIEraseSector(sec);
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

sdk_SpiFlashOpResult IRAM sdk_spi_flash_write(uint32_t des_addr, uint32_t *src_addr, uint32_t size) {
    sdk_SpiFlashOpResult result;

    if (!src_addr) {
        return SPI_FLASH_RESULT_ERR;
    }
    if (size & 3) {
        size = (size & ~3) + 4;
    }
    portENTER_CRITICAL();
    Cache_Read_Disable();
    result = sdk_SPIWrite(des_addr, src_addr, size);
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

sdk_SpiFlashOpResult IRAM sdk_spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size) {
    sdk_SpiFlashOpResult result;

    if (!des_addr) {
        return SPI_FLASH_RESULT_ERR;
    }
    portENTER_CRITICAL();
    Cache_Read_Disable();
    result = sdk_SPIRead(src_addr, des_addr, size);
    Cache_Read_Enable(0, 0, 1);
    portEXIT_CRITICAL();
    return result;
}

#endif /* OPEN_LIBMAIN_SPI_FLASH */