// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "sdkconfig.h"
#include "esp_attr.h"
#include "spi_flash.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp8266/eagle_soc.h"

#define PERIPHS_SPI_FLASH_USRREG        (0x60000200 + 0x1c)
#define PERIPHS_SPI_FLASH_CTRL          (0x60000200 + 0x08)
#define PERIPHS_IO_MUX_CONF_U           (0x60000800)

#define SPI0_CLK_EQU_SYSCLK             BIT8
#define SPI_FLASH_CLK_EQU_SYSCLK        BIT12

typedef struct flash_hdr {
    uint8_t         magic;
    uint8_t         blocks;
    uint8_t         spi_mode;
    uint8_t         spi_speed : 4;
    uint8_t         spi_size_map : 4;
    uint32_t        entry_addr;
} flash_hdr_t;

typedef struct boot_hdr {
    uint8_t     user_bin : 2;
    uint8_t     boot_status : 1;
    uint8_t     to_qio : 1;
    uint8_t     reserve : 4;

    uint8_t     version : 5;
    uint8_t     test_pass_flag : 1;
    uint8_t     test_start_flag : 1;
    uint8_t     enhance_boot_flag : 1;

    uint8_t     test_bin_addr[3];
    uint8_t     user_bin_addr[3];
} boot_hdr_t;

extern int ets_printf(const char *fmt, ...);

static const char *TAG = "chip_boot";

/*
 * @brief initialize the chip including flash I/O and chip cache according to
 *        boot parameters which are stored at the flash
 */
void chip_boot(size_t start_addr, size_t map)
{
    int ret;
    uint32_t freqdiv, flash_size, sect_size;
    uint32_t freqbits;
    flash_hdr_t fhdr;
    boot_hdr_t bhdr;

    uint32_t flash_map_table[FALSH_SIZE_MAP_MAX] = {
        1 * 1024 * 1024,
        2 * 1024 * 1024,
        4 * 1024 * 1024,
        8 * 1024 * 1024,
        16 * 1024 * 1024
    };
    uint32_t flash_map_table_size = sizeof(flash_map_table) / sizeof(flash_map_table[0]);

    extern void phy_get_bb_evm(void);
    extern void cache_init(uint32_t , uint32_t, uint32_t);
    extern void user_spi_flash_dio_to_qio_pre_init(void);
    extern int esp_get_boot_param(uint32_t, uint32_t, void *, uint32_t);

    phy_get_bb_evm();

    SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_USRREG, BIT5);

    ret = spi_flash_read(start_addr, &fhdr, sizeof(flash_hdr_t));
    if (ret) {
        ESP_LOGE(TAG, "SPI flash read result %d\n", ret);
    }

    if (3 > fhdr.spi_speed)
        freqdiv = fhdr.spi_speed + 2;
    else if (0x0F == fhdr.spi_speed)
        freqdiv = 1;
    else
        freqdiv = 2;

    if (fhdr.spi_size_map < flash_map_table_size) {
        flash_size = flash_map_table[fhdr.spi_size_map];
    } else {
        flash_size = 0; 
        ESP_LOGE(TAG, "SPI size error is %d\n", fhdr.spi_size_map);
    }
    sect_size = 4 * 1024;

    if (1 >= freqdiv) {
        freqbits = SPI_FLASH_CLK_EQU_SYSCLK;
        SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_FLASH_CLK_EQU_SYSCLK);
        SET_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYSCLK);
    } else {
        freqbits = ((freqdiv - 1) << 8) + ((freqdiv / 2 - 1) << 4) + (freqdiv - 1);
        CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_FLASH_CLK_EQU_SYSCLK);
        CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYSCLK);
    }
    SET_PERI_REG_BITS(PERIPHS_SPI_FLASH_CTRL, 0xfff, freqbits, 0);

    ret = esp_get_boot_param(flash_size, sect_size, &bhdr, sizeof(boot_hdr_t));
    if (ret) {
        ESP_LOGE(TAG, "Get boot parameters %d\n", ret);
    }

    cache_init(map, 0, 0);

    if (bhdr.to_qio == 0)
        user_spi_flash_dio_to_qio_pre_init();
}