/**************************************************************************
 * Copyright (C) 2020-2020  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : user_gpio.c
 * Author      : yuanshifeng@unisound.com
 * Date        : 2021.10.23
 *
 **************************************************************************/
#include "user_flash.h"
#include <driver/uni_flash.h>
#include <driver/uni_irq.h>
#include <lvp_buffer.h>
#include "lvp_attr.h"
#include "printf.h"
#include "board_config.h"


#define LOG_TAG "[user_flash]"

static UNI_FLASH_DEV *g_user_flash_dev = NULL;
static unsigned int g_chip_size = 0;
static unsigned int g_erase_size = 0;

DRAM0_STAGE2_SRAM_ATTR int user_flash_init(void) {
  if (NULL != g_user_flash_dev) {
    printf(LOG_TAG "warning: user flash inited already!\n");
    return 0;
  }
  unsigned int irq_state = uni_lock_irq_save();
  g_user_flash_dev = uni_spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
                                       CONFIG_SF_DEFAULT_CS,
                                       CONFIG_SF_DEFAULT_SPEED,
                                       CONFIG_SF_DEFAULT_MODE);
  if (NULL == g_user_flash_dev) {
    printf(LOG_TAG "error: user flash init failed!\n");
    uni_unlock_irq_restore(irq_state);
    return -1;
  }
  unsigned int flash_id = uni_spi_flash_getinfo(g_user_flash_dev,
                                                UNI_FLASH_CHIP_ID);
  printf(LOG_TAG "user flash id is 0x%X\n", flash_id);
  
  g_erase_size = uni_spi_flash_getinfo(g_user_flash_dev, UNI_FLASH_ERASE_SIZE);
  g_chip_size = uni_spi_flash_getinfo(g_user_flash_dev, UNI_FLASH_CHIP_SIZE);
  printf(LOG_TAG "user flash max size is 0x%X\n", g_chip_size);
  printf(LOG_TAG "user flash erase size is 0x%X\n", g_erase_size);
  if (!g_erase_size || !g_chip_size) {
    printf(LOG_TAG "error: user flash init failed!\n");
    g_user_flash_dev = NULL;
    uni_unlock_irq_restore(irq_state);
    return -1;
  } else {
    uni_unlock_irq_restore(irq_state);
    return 0;
  }
}

DRAM0_STAGE2_SRAM_ATTR int user_flash_read(unsigned int addr,
                                           unsigned char *data,
                                           unsigned int len) {
  if (NULL == g_user_flash_dev) {
    printf(LOG_TAG "err: user flash is not inited!\n");
    return -1;
  }
  if (addr + len >= g_chip_size) {
    printf(LOG_TAG "err: invalid params!\n");
    return -1;
  }
  unsigned int irq_state = uni_lock_irq_save();
  int ret = uni_spi_flash_readdata(g_user_flash_dev, addr, data, len);
  uni_unlock_irq_restore(irq_state);
  return ret;
}

DRAM0_STAGE2_SRAM_ATTR int user_flash_erase(unsigned int addr,
                                            unsigned int len) {
  if (NULL == g_user_flash_dev) {
    printf(LOG_TAG "err: user flash is not inited!\n");
    return -1;
  }
  if (addr + len >= g_chip_size) {
    printf(LOG_TAG "err: invalid params!\n");
    return -1;
  }
  if (addr % g_erase_size || len % g_erase_size) {
    printf(LOG_TAG "err: erase addr&len should be n * %d bytes!\n",
           g_erase_size);
    return -1;
  }
  unsigned int irq_state = uni_lock_irq_save();
  int ret = uni_spi_flash_erasedata(g_user_flash_dev, addr, len);
  uni_unlock_irq_restore(irq_state);
  return ret;
}

DRAM0_STAGE2_SRAM_ATTR int user_flash_write(unsigned int addr,
                                            unsigned char *data,
                                            unsigned int len) {
  if (NULL == g_user_flash_dev) {
    printf(LOG_TAG "err: user flash is not inited!\n");
    return -1;
  }
  if (addr + len >= g_chip_size) {
    printf(LOG_TAG "err: invalid params!\n");
    return -1;
  }
  unsigned int irq_state = uni_lock_irq_save();
  int ret = uni_spi_flash_pageprogram(g_user_flash_dev, addr, data, len);
  uni_unlock_irq_restore(irq_state);
  return ret;
}

void user_flash_test(void) {
  unsigned char tmp_buf[512];
  printf("\n----------------ff-----------------------\n");
  user_flash_erase(0x7D000, 0x1000);
  user_flash_read(0x7D000, tmp_buf, 512);
  for (int i = 0; i < 256; i++) {
    printf("%02X ", tmp_buf[i]);
    if (i % 16 == 15) printf("\n");
  }

  printf("\n------------------5a a5 27---------------------\n");
  memset(tmp_buf, 0x5A, sizeof(tmp_buf));
  user_flash_write(0x7D000, tmp_buf, 100);
  memset(tmp_buf, 0xA5, sizeof(tmp_buf));
  user_flash_write(0x7D000 + 100, tmp_buf, 100);
  memset(tmp_buf, 0x27, sizeof(tmp_buf));
  user_flash_write(0x7D000 + 200, tmp_buf, 100);
  user_flash_read(0x7D000, tmp_buf, 512);
  for (int i = 0; i < 256; i++) {
    printf("%02X ", tmp_buf[i]);
    if (i % 16 == 15) printf("\n");
  }
  user_flash_erase(0x7D000, 0x1000);
  printf("\n-----------------f4 4f----------------------\n");
  memset(tmp_buf, 0xF4, sizeof(tmp_buf));
  user_flash_write(0x7D000, tmp_buf, 100);
  memset(tmp_buf, 0x4F, sizeof(tmp_buf));
  user_flash_write(0x7D000 + 100, tmp_buf, 100);
  user_flash_read(0x7D000, tmp_buf, 512);
  for (int i = 0; i < 256; i++) {
    printf("%02X ", tmp_buf[i]);
    if (i % 16 == 15) printf("\n");
  }
}

