/****************************************************************************
 * arch/arm/src/lc823450/lc823450_spifi2.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/arch.h>

#include <errno.h>

#include "chip.h"
#include "arm_internal.h"
#include "lc823450_syscontrol.h"
#include "lc823450_spifi2.h"

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: wait_txfifo_empty
 ****************************************************************************/

static int wait_txfifo_empty(void)
{
  int t;
  t = 1000;

  while ((getreg32(SF_FIFO_NUM) & SF_FIFO_NUM_T_MASK) && t)
    {
      up_udelay(1);
      t--;
    }

  return t == 0 ? -ETIMEDOUT : 0;
}

/****************************************************************************
 * Name: wait_rxfifo_notempty
 ****************************************************************************/

static int wait_rxfifo_notempty(void)
{
  int t;
  t = 1000;

  while (!(getreg32(SF_FIFO_NUM) & SF_FIFO_NUM_R_MASK) && t)
    {
      up_udelay(1);
      t--;
    }

  return t == 0 ? -ETIMEDOUT : 0;
}

/****************************************************************************
 * Name: spiflash_cmd_only
 ****************************************************************************/

static int spiflash_cmd_only(int cmd)
{
  /* COMMAND(1byte) = 1 */

  putreg32(SF_SIZE_NOREAD | 1 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(0, SF_DUMMY);
  putreg8(cmd, SF_T_FIFO);

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);
  return wait_txfifo_empty();
}

/****************************************************************************
 * Name: spiflash_read_jdid
 ****************************************************************************/

static int spiflash_read_jdid(void)
{
  int ret = 0;

  /* COMMAND(1byte) + jid(3byte) */

  putreg32(1 << SF_SIZE_UL_SHIFT | 4 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(SF_DUMMY_DUMMY, SF_DUMMY);

  /* COMMAND */

  putreg8(SF_CMD_READ_JID, SF_T_FIFO);

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  /* MID */

  wait_rxfifo_notempty();
  ret |= getreg8(SF_R_FIFO);

  /* MType */

  wait_rxfifo_notempty();
  ret <<= 8;
  ret |= getreg8(SF_R_FIFO);

  /* CapID */

  wait_rxfifo_notempty();

  ret <<= 8;
  ret |= getreg8(SF_R_FIFO);

  return ret;
}

/****************************************************************************
 * Name: spiflash_quad_enable_winbond
 ****************************************************************************/

static void spiflash_quad_enable_winbond(void)
{
  /* SR Write Enable */

  if (spiflash_cmd_only(SF_CMD_WRITE_EN))
    {
      return;
    }

  /* Quad Enable */

  putreg32(SF_SIZE_NOREAD | 3 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(0, SF_DUMMY);
  putreg8(SF_CMD_WRITE_STATUS, SF_T_FIFO);
  putreg8(0, SF_T_FIFO);              /* status1 */
  putreg8(SF_STATUS2_QE, SF_T_FIFO);  /* status2 */

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  wait_txfifo_empty();
}

/****************************************************************************
 * Name: spiflash_read_quad_enable_winbond
 ****************************************************************************/

static int spiflash_read_quad_enable_winbond(void)
{
  /* COMMAND(1byte) + status(1byte) */

  putreg32(1 << SF_SIZE_UL_SHIFT | 2 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(SF_DUMMY_DUMMY, SF_DUMMY);

  /* COMMAND */

  putreg8(SF_CMD_READ_STATUS2, SF_T_FIFO);

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  wait_rxfifo_notempty();

  return getreg8(SF_R_FIFO) & SF_STATUS2_QE;
}

/****************************************************************************
 * Name: spiflash_quad_enable_macronix
 ****************************************************************************/

static void spiflash_quad_enable_macronix(void)
{
  /* SR Write Enable */

  if (spiflash_cmd_only(SF_CMD_WRITE_EN))
    {
      return;
    }

  /* Quad Enable */

  putreg32(SF_SIZE_NOREAD | 2 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(0, SF_DUMMY);
  putreg8(SF_CMD_WRITE_STATUS, SF_T_FIFO);
  putreg8(1 << 6, SF_T_FIFO); /* status1 */

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  wait_txfifo_empty();
}

/****************************************************************************
 * Name: spiflash_read_quad_enable_macronix
 ****************************************************************************/

static int spiflash_read_quad_enable_macronix(void)
{
  /* COMMAND(1byte) + status(1byte) */

  putreg32(1 << SF_SIZE_UL_SHIFT | 2 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(SF_DUMMY_DUMMY, SF_DUMMY);

  /* COMMAND */

  putreg8(SF_CMD_READ_STATUS1, SF_T_FIFO);

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  wait_rxfifo_notempty();

  return getreg8(SF_R_FIFO) & (1 << 6);
}

/****************************************************************************
 * Name: spiflash_quad_enable
 ****************************************************************************/

static void spiflash_quad_enable(int jdid)
{
  if ((jdid & SF_JID_MID_MASK) == SF_JID_MID_MACRONIX)
    {
      spiflash_quad_enable_macronix();
    }
  else
    {
      spiflash_quad_enable_winbond();
    }
}

/****************************************************************************
 * Name: spiflash_read_quad_enable
 ****************************************************************************/

static int spiflash_read_quad_enable(int jdid)
{
  if ((jdid & SF_JID_MID_MASK) == SF_JID_MID_MACRONIX)
    {
      return spiflash_read_quad_enable_macronix();
    }

  return spiflash_read_quad_enable_winbond();
}

/****************************************************************************
 * Name: spiflash_write_busy
 ****************************************************************************/

static int spiflash_write_busy(void)
{
  /* COMMAND(1byte) + status(1byte) */

  putreg32(1 << SF_SIZE_UL_SHIFT | 2 << SF_SIZE_T_SHIFT, SF_SIZE);
  putreg32(SF_DUMMY_DUMMY, SF_DUMMY);

  /* COMMAND */

  putreg8(SF_CMD_READ_STATUS1, SF_T_FIFO);

  /* Start Transfer */

  putreg32(SF_CTL_ACT, SF_CTL);

  wait_rxfifo_notempty();

  return getreg8(SF_R_FIFO) & SF_STATUS1_BUSY;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: lc823450_spiflash_earlyinit
 ****************************************************************************/

void lc823450_spiflash_earlyinit(void)
{
#ifdef CONFIG_LC823450_SPIFI_QUADIO
  int jdid;
#endif

  /* S-Flash controller: Enable clock & unreset */

  modifyreg32(MCLKCNTBASIC, 0, MCLKCNTBASIC_SFIF_CLKEN);
  modifyreg32(MRSTCNTBASIC, 0, MRSTCNTBASIC_SFIF_RSTB);

  /* S-Flash cache: Enable clock & unreset */

  modifyreg32(MCLKCNTBASIC, 0, MCLKCNTBASIC_CACHE_CLKEN);
  modifyreg32(MRSTCNTBASIC, 0, MRSTCNTBASIC_CACHE_RSTB);

  /* Set S-Flash I/O voltage to 3.3v */

  putreg32(0x1, SFIFSEL);

#ifdef CONFIG_LC823450_SPIFI_QUADIO
  jdid = spiflash_read_jdid();

  /* Check if QUAD mode supported */

  if (!spiflash_read_quad_enable(jdid))
    {
      spiflash_quad_enable(jdid);
      while (spiflash_write_busy())
        {
          up_udelay(10);
        }
    }

  /* bus accelerator enable : mode 3(use FAST_READ_QUAD_IO) */

  putreg32(SF_BUS_BUSEN | 3 << SF_BUS_BUSMODE_SHIFT | SF_BUS_LOOKAHEAD,
           SF_BUS);
#else
  /* bus accelerator enable : mode 1(use FAST_READ) */

  putreg32(SF_BUS_BUSEN | 1 << SF_BUS_BUSMODE_SHIFT | SF_BUS_LOOKAHEAD,
           SF_BUS);
#endif /* CONFIG_LC823450_SPIFI_QUADIO */

  /* S-Flash cache: Flash and Re-enable cache */

  modifyreg32(CACHE_CTL, CACHE_CTL_USE, 0);
  modifyreg32(CACHE_CTL, 0, CACHE_CTL_USE);
}
