text: /**
   * @file    wm_fls.c
   *
   * @brief   flash Driver Module
   *
   * @author  dave
   *
   * Copyright (c) 2015 Winner Microelectronics Co., Ltd.
   */
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  
  #include "wm_list.h"
  #include "wm_hostspi.h"
  #include "wm_flash.h"
  #include "wm_dbg.h"
  #include "wm_mem.h"
  #include "wm_fls_gd25qxx.h"
  
  
  static struct tls_fls *spi_fls = NULL;
  
  static int tls_spifls_read_id(u32 * id)
  {
      u32 cmd;
      int err;
  
      cmd = FLS_CMD_READ_DEV_ID;
      *id = 0;
  
      err = tls_spi_read_with_cmd((const u8 *) &cmd, 4, (u8 *) id, 3);
  
      if (err != TLS_SPI_STATUS_OK)
      {
          TLS_DBGPRT_ERR("flash read ID fail(%d)!\n", err);
          return TLS_FLS_STATUS_EIO;
      }
  
      TLS_DBGPRT_FLASH_INFO("flash ID - 0x%x.\n", *id);
  
      return TLS_FLS_STATUS_OK;
  }
  
  
  /**
   * @brief          This function is used to read data from the flash.
   *
   * @param[in]      addr                 is byte offset addr for read from the flash.
   * @param[in]      buf                   is user for data buffer of flash read
   * @param[in]      len                   is byte length for read.
   *
   * @retval         TLS_FLS_STATUS_OK	    if read sucsess
   * @retval         TLS_FLS_STATUS_EIO	    if read fail
   *
   * @note           None
   */
  int tls_spifls_read(u32 addr, u8 * buf, u32 len)
  {
      int err;
      u32 read_bytes;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if ((addr >= spi_fls->current_drv->total_size) || (len == 0)
          || (buf == NULL))
      {
          return TLS_FLS_STATUS_EINVAL;
      }
  
      drv = spi_fls->current_drv;
      read_bytes =
          ((addr + len) > drv->total_size) ? (drv->total_size - addr) : len;
  
      err = TLS_FLS_STATUS_OK;
  
      err = drv->read(addr, buf, read_bytes);
  
      return err;
  }
  
  int tls_spifls_fast_read(u32 addr, u8 * buf, u32 len)
  {
      int err;
      u32 read_bytes;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if ((addr >= spi_fls->current_drv->total_size) || (len == 0)
          || (buf == NULL))
      {
          return TLS_FLS_STATUS_EINVAL;
      }
      if ((spi_fls->current_drv->flags & TLS_FLS_FLAG_FAST_READ) !=
          TLS_FLS_FLAG_FAST_READ)
      {
          return TLS_FLS_STATUS_ENOSUPPORT;
      }
  
      drv = spi_fls->current_drv;
      read_bytes =
          ((addr + len) > drv->total_size) ? (drv->total_size - addr) : len;
  
      err = TLS_FLS_STATUS_OK;
  
      err = drv->fast_read(addr, buf, read_bytes);
  
      return err;
  }
  
  int tls_spifls_page_write(u32 page, u8 * buf, u32 page_cnt)
  {
      int err;
      u32 write_pages;
      u32 i;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if ((page >= (spi_fls->current_drv->total_size / spi_fls->current_drv->page_size))
          || (page_cnt == 0) || (buf == NULL))
      {
          return TLS_FLS_STATUS_EINVAL;
      }
  
      drv = spi_fls->current_drv;
      write_pages = ((page + page_cnt) > (drv->total_size / drv->page_size)) ?
                      ((drv->total_size / drv->page_size) -page) : page_cnt;
  
      err = TLS_FLS_STATUS_OK;
  
      for (i = 0; i < write_pages; i++)
      {
          err = drv->page_write(page + i, buf + i * drv->page_size);
          if (err != TLS_FLS_STATUS_OK)
          {
              TLS_DBGPRT_ERR("flash page write fail(page %d)!\n", (page + i));
              break;
          }
      }
  
      return err;
  }
  
  /**
   * @brief          This function is used to write data to the flash.
   *
   * @param[in]      addr     is byte offset addr for write to the flash
   * @param[in]      buf       is the data buffer want to write to flash
   * @param[in]      len       is the byte length want to write
   *
   * @retval         TLS_FLS_STATUS_OK	           if write flash success
   * @retval         TLS_FLS_STATUS_EPERM	    if flash struct point is null
   * @retval         TLS_FLS_STATUS_ENODRV	    if flash driver is not installed
   * @retval         TLS_FLS_STATUS_EINVAL	    if argument is invalid
   * @retval         TLS_FLS_STATUS_EIO           if io error
   *
   * @note           None
   */
  int tls_spifls_write(u32 addr, u8 * buf, u32 len)
  {
      u8 *cache;
      int err;
      u32 sector_addr;
      u32 sector_num;
      u32 write_bytes;
      u32 i;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if ((addr >= spi_fls->current_drv->total_size) || (len == 0)
          || (buf == NULL))
      {
          return TLS_FLS_STATUS_EINVAL;
      }
  
      drv = spi_fls->current_drv;
      write_bytes =
          ((addr + len) > drv->total_size) ? (drv->total_size - addr) : len;
      sector_addr = addr / drv->sector_size;
      sector_num = (addr + write_bytes - 1) / drv->sector_size - sector_addr + 1;
  
      TLS_DBGPRT_FLASH_INFO
          ("write to flash: sector address - %d, sectors - %d.\n", sector_addr,
           sector_num);
  
      err = TLS_FLS_STATUS_OK;
  
      cache = tls_mem_alloc(drv->sector_size);
      if (cache == NULL)
      {
          TLS_DBGPRT_ERR("allocate sector cache memory(%dB) fail!\n",
                         drv->sector_size);
          return TLS_FLS_STATUS_ENOMEM;
      }
  
      for (i = 0; i < sector_num; i++)
      {
          TLS_DBGPRT_FLASH_INFO("firstly, read the sector - %d to cache.\n",
                                sector_addr + i);
          err = drv->read((sector_addr + i) * drv->sector_size, cache, drv->sector_size);
          if (err != TLS_FLS_STATUS_OK)
          {
              TLS_DBGPRT_ERR("flash read fail(sector %d)!\n", (sector_addr + i));
              break;
          }
  
  		if (1 == sector_num){/*flash write only in one sector*/
  			MEMCPY(cache + (addr%drv->sector_size), buf, write_bytes);	
  			buf += write_bytes;
  			write_bytes = 0;			
  		}else{/*flash write through some sectors*/
  			if (0 == i) {
  				MEMCPY(cache+(addr%drv->sector_size), buf, drv->sector_size - (addr%drv->sector_size));
  				buf += drv->sector_size - (addr%drv->sector_size);
  				write_bytes -= drv->sector_size - (addr%drv->sector_size);
  			} else if (i == (sector_num - 1)) {
  				MEMCPY(cache, buf, write_bytes);
  				buf += write_bytes;
  				write_bytes = 0;
  			} else {
  				MEMCPY(cache, buf, drv->sector_size);
  				buf += drv->sector_size;
  				write_bytes -= drv->sector_size;
  			}
  		}
  
          TLS_DBGPRT_FLASH_INFO("second, erase the sector - %d.\n",
                                sector_addr + i);
          err = drv->erase(sector_addr + i);
          if (err != TLS_FLS_STATUS_OK)
          {
              TLS_DBGPRT_ERR("flash erase fail(sector %d)!\n", (sector_addr + i));
              break;
          }
  
          TLS_DBGPRT_FLASH_INFO
              ("finnaly, write the data in cache to the sector - %d.\n",
               sector_addr + i);
          err = tls_spifls_page_write((sector_addr +i) * (drv->sector_size / drv->page_size),
                              cache, drv->sector_size / drv->page_size);
          if (err != TLS_FLS_STATUS_OK)
          {
              TLS_DBGPRT_ERR("flash write fail(sector %d)!\n", (sector_addr + i));
              break;
          }
      }
  
      tls_mem_free(cache);
  
      return err;
  }
  
  
  int tls_spifls_erase(u32 sector)
  {
      int err;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if (sector >= (spi_fls->current_drv->total_size / spi_fls->current_drv->sector_size))
      {
          TLS_DBGPRT_ERR("the sector to be erase overflow!\n");
          return TLS_FLS_STATUS_EINVAL;
      }
  
      drv = spi_fls->current_drv;
  
      err = TLS_FLS_STATUS_OK;
  
      err = drv->erase(sector);
  
      return err;
  }
  
  int tls_spifls_chip_erase(void)
  {
      int err;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      drv = spi_fls->current_drv;
  
      err = TLS_FLS_STATUS_OK;
  
      err = drv->chip_erase();
  
      return err;
  }
  
  int tls_spifls_get_param(u8 type, void *param)
  {
      int err;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash driver not installed!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      if (param == NULL)
      {
          return TLS_FLS_STATUS_EINVAL;
      }
  
      drv = spi_fls->current_drv;
      err = TLS_FLS_STATUS_OK;
      switch (type)
      {
          case TLS_FLS_PARAM_TYPE_ID:
              *((u32 *) param) = drv->id;
              break;
  
          case TLS_FLS_PARAM_TYPE_SIZE:
              *((u32 *) param) = drv->total_size;
              break;
  
          case TLS_FLS_PARAM_TYPE_PAGE_SIZE:
              *((u32 *) param) = drv->page_size;
              break;
  
          case TLS_FLS_PARAM_TYPE_PROG_SIZE:
              *((u32 *) param) = drv->program_size;
              break;
  
          case TLS_FLS_PARAM_TYPE_SECTOR_SIZE:
              *((u32 *) param) = drv->sector_size;
              break;
  
          default:
              TLS_DBGPRT_WARNING("invalid parameter ID!\n");
              err = TLS_FLS_STATUS_EINVAL;
              break;
      }
  
      return err;
  }
  
  int tls_spifls_drv_register(struct tls_fls_drv *fls_drv)
  {
      u32 cpu_sr;
      struct tls_fls_drv *drv;
  
      if (fls_drv == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EINVAL;
      }
  
      dl_list_for_each(drv, &spi_fls->fls_drvs, struct tls_fls_drv, drv_list)
      {
          if (drv->id == fls_drv->id)
          {
              TLS_DBGPRT_WARNING
                  ("corresponding spi flash driver has registered!\n");
              return TLS_FLS_STATUS_EEXIST;
          }
      }
  
      dl_list_add_tail((struct dl_list *) &spi_fls->fls_drvs,
                       (struct dl_list *) &fls_drv->drv_list);
  
      TLS_DBGPRT_FLASH_INFO("the spi flash driver is registered successfully!\n");
  
      return TLS_FLS_STATUS_OK;
  }
  
  int tls_spifls_drv_unregister(struct tls_fls_drv *fls_drv)
  {
      TLS_DBGPRT_WARNING
          ("unregister spi flash driver operation is not supported!\n");
      return TLS_FLS_STATUS_EPERM;
  }
  
  int tls_spifls_probe(void)
  {
      int err;
      u32 id;
      struct tls_fls_drv *drv;
  
      if (spi_fls == NULL)
      {
          TLS_DBGPRT_ERR("flash driver module not beed installed!\n");
          return TLS_FLS_STATUS_EPERM;
      }
      if (spi_fls->current_drv != NULL)
      {
          TLS_DBGPRT_ERR("the current spi flash has fount the matched driver!\n");
          return TLS_FLS_STATUS_EBUSY;
      }
  
      TLS_DBGPRT_FLASH_INFO("try to read the current spi flash ID.\n");
      err = tls_spifls_read_id(&id);
      if (err != TLS_FLS_STATUS_OK)
      {
          return err;
      }
  
      TLS_DBGPRT_FLASH_INFO("current spi flash ID - 0x%x.\n", id);
  
      dl_list_for_each(drv, &spi_fls->fls_drvs, struct tls_fls_drv, drv_list)
      {
          err = drv->probe(id);
          if (err != TLS_FLS_STATUS_OK)
          {
              return err;
          }
  
          tls_spi_setup(drv->mode, drv->cs_active, drv->clock);
  
          TLS_DBGPRT_FLASH_INFO("matched the spi flash driver.\n");
          spi_fls->current_drv = drv;
          break;
      }
  
      if (spi_fls->current_drv == NULL)
      {
          TLS_DBGPRT_WARNING("not found the matched spi flash driver!\n");
          return TLS_FLS_STATUS_ENODRV;
      }
  
      return TLS_FLS_STATUS_OK;
  }
  
  int tls_spifls_init(void)
  {
      struct tls_fls *fls;
      int err;
  
      if (spi_fls != NULL)
      {
          TLS_DBGPRT_ERR("flash driver module has been installed!\n");
          return TLS_FLS_STATUS_EBUSY;
      }
  
      fls = (struct tls_fls *) tls_mem_alloc(sizeof(struct tls_fls));
      if (fls == NULL)
      {
          TLS_DBGPRT_ERR("allocate @spi_fls fail!\n");
          return TLS_FLS_STATUS_ENOMEM;
      }
  
      memset(fls, 0, sizeof(*fls));
      dl_list_init((struct dl_list *) &fls->fls_drvs);
  
      spi_fls = fls;
  
      tls_spifls_drv_install();
  
      return TLS_FLS_STATUS_OK;
  }
  
  int tls_spifls_exit(void)
  {
      TLS_DBGPRT_FLASH_INFO("Not support flash driver module uninstalled!\n");
      return TLS_FLS_STATUS_EPERM;
  }
  