text: /*
   * Copyright (c) 2012, Freescale Semiconductor, Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   * o Redistributions of source code must retain the above copyright notice, this list
   *   of conditions and the following disclaimer.
   *
   * o Redistributions in binary form must reproduce the above copyright notice, this
   *   list of conditions and the following disclaimer in the documentation and/or
   *   other materials provided with the distribution.
   *
   * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
   *   contributors may be used to endorse or promote products derived from this
   *   software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #if !defined(__gpmi_h_)
  #define __gpmi_h_
  
  #include "sdk.h"
  #include "bch_ecc.h"
  
  /*!
   * @file gpmi.h
   * @brief The header file that contains the general definitions for NAND
   *
   * @ingroup diag_nand
   */
  
  //! @addtogroup diag_nand
  //! @{
  
  #pragma pack(1)
  //! @brief NAND timings for setting up the GPMI timing.
  //!
  //! This structure holds the timings for the NAND.  This data is used by
  //! gpmi_set_timings() to setup the GPMI hardware registers.
  typedef struct GpmiNandTimings
  {
      //! @brief The data setup time (tDS), in nanoseconds.
      uint8_t tDS;
  
      //! @brief The data hold time (tDH), in nanoseconds.
      uint8_t tDH;
  
      //! @brief The address setup time (tSU), in nanoseconds.
      //!
      //! This value amalgamates the NAND parameters tCLS, tCS, and tALS.
      uint8_t tSU;
  
      //! @brief From the NAND datasheet.
      uint8_t tREA;
  
      //! @brief From the NAND datasheet.
      //!
      //! This is the amount of time that the last contents of the data lines will persist
      //! after the controller drives the -RE signal true.
      //!
      //! EDO Mode: This time is from the NAND spec, and the persistence of data
      //! is determined by (tRLOH + tDH).
      //!
      //! Non-EDO Mode: This time is ignored, because the persistence of data
      //! is determined by tRHOH.
      uint8_t tRLOH;
  
      //! @brief From the NAND datasheet.
      //!
      //! This is the amount of time that the last contents of the data lines will persist after the
      //! controller drives the -RE signal false.
      //!
      //! EDO Mode: This time is ignored, because the persistence of data is determined by
      //! (tRLOH + tDH).
      //!
      //! Non-EDO Mode: This time is totally due to capacitive effects of the hardware. For reliable
      //! behavior it should be set to zero, unless there is specific knowledge of the trace
      //! capacitance and the persistence of the data values.
      uint8_t tRHOH;
  } GpmiNandTimings_t;
  #pragma pack()
  
  // Forward declaration.
  typedef struct _apbh_dma_t apbh_dma_t;
  
  //! @brief DMA wait options.
  //!
  //! These mask values are combined together to form the wait mask that is passed to gpmi_run_dma().
  //! The wait mask indicates which IRQs must fire before the DMA is considered done executing.
  enum _nand_gpmi_dma_wait_mask {
      kNandGpmiDmaWaitMask_Nothing = 0,   //!< Do not wait.
      kNandGpmiDmaWaitMask_GpmiDma = 1,   //!< Wait for the APBH DMA completion IRQ.
      kNandGpmiDmaWaitMask_Ecc = 2,       //!< Wait for the BCH completion IRQ.
  };
  
  //! @brief APBH DMA channel for GPMI chip select 0.
  #define NAND0_APBH_CH (0)
  
  //! @brief Number of NAND chip selects supported by this chip.
  //!
  //! This is also implicitly the number of DMA channels in the APBH.
  #define GPMI_CHIP_SELECT_COUNT (4)
  
  //! @name Error codes
  //@{
  #if !defined(SUCCESS)
  #define SUCCESS (0)
  #endif
  
  #define ERROR_DDI_NAND_GPMI_NOT_PRESENT (1)
  #define ERROR_DDI_NAND_GPMI_DMA_TIMEOUT (2)
  #define ERROR_DDI_NAND_GPMI_DMA_BUSY (3)
  #define ERROR_DDI_NAND_GPMI_UNCORRECTABLE_ECC (4)
  #define ERROR_DDI_NAND_HAL_WRITE_FAILED (5)
  //@}
  
  ////////////////////////////////////////////////////////////////////////////////
  // Prototypes
  ////////////////////////////////////////////////////////////////////////////////
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  //! @name Init and shutdown
  //@{
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Enable and initialize the GPMI driver.
  //!
  //! This function configures the GPMI block using the HW registers based upon
  //! the desired chip and the number of bits. You must call this API for each
  //! chip select that will be used in the application configuration.
  //!
  //! @param[in] minDelay Minimum signal propagation delay in nanoseconds. Pass 0 to use the
  //!     default of 5 ns.
  //! @param[in] maxDelay Maximum signal propagation delay in nanoseconds. Pass 0 to use the
  //!     default of 9 ns.
  //!
  //! @return SUCCESS
  //! @return ERROR_DDI_NAND_GPMI_NOT_PRESENT
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_init(uint32_t minDelay, uint32_t maxDelay);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Resets the GPMI block.
  //!
  //! A soft reset can take multiple clocks to complete, so do NOT gate the
  //! clock when setting soft reset. The reset process will gate the clock
  //! automatically. Poll until this has happened before subsequently
  //! clearing soft reset and clock gate.
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_soft_reset(void);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Disable the GPMI driver.
  //!
  //! This function gates the clock to the GPMI peripheral.
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_disable(void);
  
  //@}
  
  //! @name Write enable
  //@{
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Enable writes via the Write Protect line of the NAND.
  //!
  //! Enable or disable writes via the /WP pin of the NAND.  This WP line is
  //! shared amongst all the NANDs.
  //!
  //! @param[in] doEnable Enable writes (1) or Disable writes (0) (/WP pin)
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_enable_writes(bool doEnable);
  
  //@}
  
  //! @name DMA Utilities
  //@{
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Synchronously execute a DMA descriptor chain.
  //!
  //! Starts a NAND DMA command associated with the chip select
  //! in the @a chipSelect parameter. Once the DMA is running, this function waits
  //! for the interrupt handler(s) to signal completion.
  //!
  //! @param[in] theDma Pointer to dma command structure. Must be the virtual address,
  //!     as it is converted to a physical address before the DMA is started.
  //! @param[in] chipSelect Which NAND chip select should be started.
  //! @param[in] waitMask A bitmask used to indicate criteria for terminating the DMA.
  //!     See #_nand_gpmi_dma_wait_mask for more info.
  //! @param[in] timeout Number of microseconds to wait before timing out. If
  //!     zero is passed for the timeout, then this function will wait forever.
  //!
  //! @retval SUCCESS The DMA is started.
  //! @retval ERROR_DDI_NAND_GPMI_DMA_BUSY Another DMA is already running.
  //! @retval ERROR_DDI_NAND_GPMI_DMA_TIMEOUT DMA never completed or is still
  //!     running. This value is returned explicitly from this function when
  //!     the DMA semphore times out, and most DMA chains also return this
  //!     error (see below for how) when the GPMI device busy timeout expires.
  //!
  //! @note Uses the BAR field of the last DMA command to signal                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
  //!             result of the DMA chain.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_run_dma(apbh_dma_t * theDma, unsigned chipSelect, uint16_t waitMask, uint32_t timeout);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Returns a Boolean indicating if a DMA is currently running.
  //!
  //! @param u32NANDDeviceNum Specifies which DMA channel to inspect.
  //! @retval true A DMA is currently running.
  //! @retval false The DMA channel is free.
  ////////////////////////////////////////////////////////////////////////////////
  bool gpmi_is_dma_active(uint32_t u32NANDDeviceNum);
  
  //@}
  
  //! @name Timing Control Functions
  //@{
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Fill the given timing structure with the safe timings.
  //!
  //! This function is used to get the timings characteristics that work with
  //! every device we've ever known. These timings should be used during initialization
  //! and device discovery. Once the device type is known,  timings specific to
  //! that device should be set.  Remember to actually set the safe timings once
  //! you get them by calling ddi_gpmi_set_timings().
  //!
  //! @param[out] timings The timings structure that will be set to the safe
  //!     timings upon return.
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_get_safe_timings(GpmiNandTimings_t * timings);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Set the flash timing for optimal NAND performance.
  //!
  //! Set the optimal NAND timings based upon the passed in NAND timings and the
  //! current GPMI clock.
  //!
  //! @param[in] newTimings Pointer to timing table for the NAND.
  //!
  //! @warning This function assumes all NAND I/O is halted.
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_set_timings(const GpmiNandTimings_t * newTimings);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Set the timeout value for wait for ready.
  //!
  //! The timeout value set here is used for the GPMI wait for ready mode. It
  //! will have the most effect upon DMA operations.
  //!
  //! @param[in] busyTimeout Timeout value in microseconds.
  ////////////////////////////////////////////////////////////////////////////////
  void gpmi_set_busy_timeout(uint32_t busyTimeout);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Returns the current GPMI timings values.
  //!
  //! @return A pointer to the current timing values used by the GPMI block.
  //!     This will be the last set of timings passed to gpmi_set_timings().
  ////////////////////////////////////////////////////////////////////////////////
  const GpmiNandTimings_t * gpmi_get_current_timings();
  
  //@}
  
  //! @name Common NAND operations
  //!
  //! These functions are used to perform the common set of NAND read and write operations. They
  //! will build an appropriate DMA chain and execute it synchronously.
  //@{
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Configure the driver for a particular NAND type.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_configure(unsigned rowBytes, unsigned columnBytes, unsigned pagesPerBlock, const BchEccLayout_t * ecc);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Reset the NAND.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_reset(unsigned chipSelect);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Read a NAND's ID.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_read_id(unsigned chipSelect, uint8_t * resultID);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Erase a block on a NAND.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_erase_block(unsigned chipSelect, uint32_t blockNumber);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Read raw bytes from a NAND page.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_read_raw(unsigned chipSelect, uint32_t pageNumber, uint8_t * buffer, uint32_t offset, uint32_t count);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Write a NAND page without using ECC.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_write_raw(unsigned chipSelect, uint32_t pageNumber, const uint8_t * buffer, uint32_t offset, uint32_t count);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Read a NAND page using ECC.
  //! @bug Not yet functional.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_read_page(unsigned chipSelect, uint32_t pageNumber, uint8_t * buffer, uint8_t * auxBuffer);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Read the metadata from a NAND page.
  //! @bug Not yet functional.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_read_metadata(unsigned chipSelect, uint32_t pageNumber, uint8_t * buffer);
  
  ////////////////////////////////////////////////////////////////////////////////
  //! @brief Write a NAND page.
  //! @bug Not yet functional.
  ////////////////////////////////////////////////////////////////////////////////
  int gpmi_nand_write_page(unsigned chipSelect, uint32_t pageNumber, const uint8_t * buffer, const uint8_t * auxBuffer);
  
  //@}
  
  //! @name Application APIs
  //!
  //! These functions must be implemented by the application. They are intended to isolate the
  //! GPMI driver from board and application specific design choices.
  //@{
  
  //! @brief Configure GPMI pins.
  void gpmi_iomux_config(void);
  
  //! @brief External function to enable and configure the GPMI clock.
  void gpmi_nand_clk_setup(void);
  
  //! @brief Convert a virtual address to a physical address.
  static inline void * nand_virtual_to_physical(const void * virtualAddress) { return (void *)virtualAddress; }
  
  //@}
  
  #ifdef __cplusplus
  }
  #endif
  
  //! @}
  #endif //__gpmi_h_
  ////////////////////////////////////////////////////////////////////////////////
  // EOF
  ////////////////////////////////////////////////////////////////////////////////
  