/***********************license start***************
 * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
 * reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * 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.

 *   * Neither the name of Cavium Networks 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, including technical data, may be subject to U.S. export  control
 * laws, including the U.S. Export Administration Act and its  associated
 * regulations, and may be subject to export or import  regulations in other
 * countries.

 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
 * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/







/**
 * @file
 *
 * Interface to the PCI / PCIe DMA engines. These are only avialable
 * on chips with PCI / PCIe.
 *
 * <hr>$Revision: 49448 $<hr>
 */

#ifndef __CVMX_DMA_ENGINES_H__
#define __CVMX_DMA_ENGINES_H__

#ifdef	__cplusplus
extern "C" {
#endif

typedef enum
{
    CVMX_DMA_ENGINE_TRANSFER_OUTBOUND = 0,  /**< OUTBOUND (read from L2/DRAM, write into PCI / PCIe memory space) */
    CVMX_DMA_ENGINE_TRANSFER_INBOUND = 1,   /**< INBOUND (read from PCI / PCIe memory space, write into L2/DRAM) */
    CVMX_DMA_ENGINE_TRANSFER_INTERNAL = 2,  /**< INTERNAL-ONLY (read from L2/DRAM, write into L2/DRAM). Only available on chips with PCIe */
    CVMX_DMA_ENGINE_TRANSFER_EXTERNAL = 3,  /**< EXTERNAL-ONLY (read from PCIe memory space, write into PCIe memory space). Only available on chips with PCIe */
} cvmx_dma_engine_transfer_t;

typedef union
{
    uint64_t u64;
    struct
    {
        uint64_t    reserved_60_63 :   4;   /**< Must be zero */
        uint64_t    fport           :  2;   /**< First port. FPort indicates the physical PCIe port used for the
                                                PCIe memory space pointers in the FIRST POINTERS block in the
                                                EXTERNAL-ONLY case. Must be zero in the OUTBOUND, INBOUND and
                                                INTERNAL-ONLY cases. Must be zero on chips with PCI */
        uint64_t    lport           :  2;   /**< Last port. LPort indicates the physical PCIe port used for the
                                                PCIe memory space pointers in the LAST POINTERS block in the
                                                OUTBOUND, INBOUND, and EXTERNAL-ONLY cases. Must be zero in the
                                                INTERNAL-ONLY case. Must be zero on chips with PCI */
        cvmx_dma_engine_transfer_t type :  2; /**< Type ­ A given PCI DMA transfer is either OUTBOUND (read from L2/DRAM,
                                                write into PCI / PCIe memory space), INBOUND (read from PCI / PCIe memory space, write
                                                into L2/DRAM), INTERNAL-ONLY (read from L2/DRAM, write into L2/DRAM), or
                                                EXTERNAL-ONLY (read from PCIe memory space, write into PCIe memory space). */
        uint64_t    wqp             :  1;   /**< Work-queue pointer. When WQP = 1, PTR (if non-zero) is a pointer to a
                                                work-queue entry that is submitted by the hardware after completing the DMA;
                                                when WQP = 0, PTR (if non-zero) is a pointer to a byte in local memory that
                                                is written to 0 by the hardware after completing the DMA. */
        uint64_t    c               :  1;   /**< C ­ Counter. 1 = use counter 1, 0 = use counter 0.
                                                The C bit selects between the two counters (NPEI_DMA_CNTS[DMA0,DMA1])
                                                that can optionally be updated after an OUTBOUND or EXTERNAL-ONLY
                                                transfer, and also selects between the two forced-interrupt bits
                                                (NPEI_INT_SUMn[DMA0_FI, DMA1_FI]) that can optionally be set after an
                                                OUTBOUND or EXTERNAL-ONLY transfer. C must be zero for INBOUND or
                                                INTERNAL-ONLY transfers. */
        uint64_t    ca              :  1;   /**< CA ­ Counter add.
                                                When CA = 1, the hardware updates the selected counter after it completes the
                                                PCI DMA OUTBOUND or EXTERNAL-ONLY Instruction.
                                                    - If C = 0, PCIE_DMA_CNT0 is updated
                                                    - If C = 1, PCIE_DMA_CNT1 is updated.
                                                Note that this update may indirectly cause
                                                NPEI_INT_SUM[DCNT0,DCNT1,DTIME0,DTIME1] to become set (depending
                                                on the NPEI_DMA*_INT_LEVEL settings), so may cause interrupts to occur on a
                                                remote PCI host.
                                                    - If NPEI_DMA_CONTROL[O_ADD1] = 1, the counter is updated by 1.
                                                    - If NPEI_DMA_CONTROL[O_ADD1] = 0, the counter is updated by the total
                                                    bytes in the transfer.
                                                When CA = 0, the hardware does not update any counters.
                                                For an INBOUND or INTERNAL-ONLY PCI DMA transfer, CA must never be
                                                set, and the hardware never adds to the counters. */
        uint64_t    fi              :  1;   /**< FI ­ Force interrupt.
                                                When FI is set for an OUTBOUND or EXTERNAL-ONLY transfer, the hardware
                                                sets a forced interrupt bit after it completes the PCI DMA Instruction. If C = 0,
                                                NPEI_INT_SUMn[DMA0_FI] is set, else NPEI_INT_SUMn[DMA1_FI] is set. For
                                                an INBOUND or INTERNAL-ONLY PCI DMA operation, FI must never be set,
                                                and the hardware never generates interrupts. */
        uint64_t    ii              :  1;   /**< II­ Ignore the I bit (i.e. the I bit of the PCI DMA instruction local pointer).
                                                For OUTBOUND transfers when II = 1, ignore the I bit and the FL bit in the
                                                DMA HDR alone determines whether the hardware frees any/all of the local
                                                buffers in the FIRST POINTERS area:
                                                    - when FL = 1, the hardware frees the local buffer when II=1.
                                                    - when FL = 0, the hardware does not free the local buffer when II=1.
                                                For OUTBOUND transfers when II = 0, the I bit in the local pointer selects
                                                whether local buffers are freed on a pointer-by-pointer basis:
                                                    - when (FL  I) is true, the hardware frees the local buffer when II=0.
                                                For INBOUND, INTERNAL-ONLY, and EXTERNAL-ONLY PCI DMA transfers,
                                                II must never be set, and local buffers are never freed. */
        uint64_t    fl              :  1;   /**< FL ­ Free local buffer.
                                                When FL = 1, for an OUTBOUND operation, it indicates that the local buffers in
                                                the FIRST BUFFERS area should be freed.
                                                If II = 1, the FL bit alone indicates whether the local buffer should be freed:
                                                    - when FL = 1, the hardware frees the local buffer when II=1.
                                                    - when FL = 0, the hardware does not free the local buffer when II=1.
                                                If II = 0, the I bit in the local pointer (refer to Section 9.5.2) determines whether
                                                the local buffer is freed:
                                                    - when (FL  I) is true, the hardware frees the local buffer when II=0.
                                                For an INBOUND, INTERNAL-ONLY, or EXTERNAL-ONLY PCI DMA transfer,
                                                FL must never be set, and local buffers are never freed. */
        uint64_t    nlst            :  4;   /**< NLST ­ Number Last pointers.
                                                The number of pointers in the LAST POINTERS area.
                                                In the INBOUND, OUTBOUND, and EXTERNAL-ONLY cases, the LAST
                                                POINTERS area contains PCI components, and the number of 64-bit words
                                                required in the LAST POINTERS area is:
                                                    - HDR.NLST + ((HDR.NLST + 3)/4) where the division removes the fraction.
                                                In the INTERNAL-ONLY case, the LAST POINTERS area contains local
                                                pointers, and the number of 64-bit words required in the LAST POINTERS area is:
                                                    - HDR.NLST
                                                Note that the sum of the number of 64-bit words in the LAST POINTERS and
                                                FIRST POINTERS area must never exceed 31. */
        uint64_t    nfst            :  4;   /**< NFST ­ Number First pointers.
                                                The number of pointers in the FIRST POINTERS area.
                                                In the INBOUND, OUTBOUND, and INTERNAL-ONLY cases, the FIRST
                                                POINTERS area contains local pointers, and the number of 64-bit words required
                                                in the FIRST POINTERS area is:
                                                    - HDR.NFST
                                                In the EXTERNAL-ONLY case, the FIRST POINTERS area contains PCI
                                                components, and the number of 64-bit words required in the FIRST POINTERS
                                                area is:
                                                    - HDR.NFST + ((HDR.NFST + 3)/4) where the division removes the fraction. */
        uint64_t    addr            : 40;   /**< PTR ­ Pointer, either a work-queue-entry pointer (when WQP = 1) or a local
                                                memory pointer (WQP = 0).
                                                When WQP = 1 and PTR  0x0, the hardware inserts the work-queue entry
                                                indicated by PTR into a POW input queue after the PCI DMA operation is
                                                complete. (Section 5.4 describes the work queue entry requirements in this
                                                case.) When WQP = 1, PTR<2:0> must be 0x0.
                                                When WQP = 0 and PTR  0x0, the hardware writes the single byte in local
                                                memory indicated by PTR to 0x0 after the PCI DMA operation is complete.
                                                NPEI_DMA_CONTROL[B0_LEND] selects the endian-ness of PTR in this
                                                case.
                                                When PTR = 0x0, the hardware performs no operation after the PCI DMA
                                                operation is complete. */
    } s;
} cvmx_dma_engine_header_t;

typedef union
{
    uint64_t u64;
    struct
    {
        uint64_t    i               :  1;   /**< I ­ Invert free.
                                                This bit gives the software the ability to free buffers independently for an
                                                OUTBOUND PCI DMA transfer. I is not used by the hardware when II is set. I
                                                must not be set, and buffers are never freed, for INBOUND, INTERNAL-ONLY,
                                                and EXTERNAL-ONLY PCI DMA transfers. */
        uint64_t    back            :  4;   /**< Back ­ Backup amount.
                                                Allows the start of a buffer that is to be freed during an OUTBOUND transfer to
                                                be different from the ptr value. Back specifies the amount to subtract from the
                                                pointer to reach the start when freeing a buffer.
                                                The address that is the start of the buffer being freed is:
                                                    - Buffer start address = ((ptr >> 7) - Back) << 7.
                                                Back is only used by the hardware when the buffer corresponding to ptr is freed.
                                                Back must be 0x0, and buffers are never freed, for INBOUND, INTERNAL-ONLY,
                                                and EXTERNAL-ONLY PCI DMA transfers. */
        uint64_t    pool            :  3;   /**< Pool ­ Free pool.
                                                Specifies which pool (of the eight hardware-managed FPA free pools) receives the
                                                buffer associated with ptr when freed during an OUTBOUND transfer.
                                                Pool is only used when the buffer corresponding to ptr is freed. Pool must be 0x0,
                                                and buffers are never freed, for INBOUND, INTERNAL-ONLY, and EXTERNAL-ONLY
                                                PCI DMA transfers. */
        uint64_t    f               :  1;   /**< F ­ Full-block writes are allowed.
                                                When set, the hardware is permitted to write all the bytes in the cache blocks
                                                covered by ptr, ptr + Size - 1. This can improve memory system performance
                                                when the write misses in the L2 cache.
                                                F can only be set for local pointers that can be written to:
                                                    - The local pointers in the FIRST POINTERS area that are write pointers for
                                                    INBOUND transfers.
                                                    - The local pointers in the LAST POINTERS area that are always write
                                                    pointers (when present for INTERNAL-ONLY transfers).
                                                F must not be set for local pointers that are not written to:
                                                    - The local pointers in the FIRST POINTERS area for OUTBOUND and
                                                    INTERNAL-ONLY transfers. */
        uint64_t    a               :  1;   /**< A ­ Allocate L2.
                                                This is a hint to the hardware that the cache blocks should be allocated in the L2
                                                cache (if they were not already). */
        uint64_t    l               :  1;   /**< L ­ Little-endian.
                                                When L is set, the data at ptr is in little-endian format rather than big-endian. */
        uint64_t    size            : 13;   /**< Size ­ Size in bytes of the contiguous space specified by ptr. A Size value of 0 is
                                                illegal. Note that the sum of the sizes in the FIRST POINTERS area must always
                                                exactly equal the sum of the sizes/lengths in the LAST POINTERS area:
                                                    - In the OUTBOUND and INBOUND cases, the HDR.NFST size fields in the
                                                    local pointers in the FIRST POINTERS area must exactly equal the lengths
                                                    of the HDR.NLST fragments in the PCI components in the LAST POINTERS
                                                    area.
                                                    - In the INTERNAL-ONLY case, the HDR.NFST size fields in the local
                                                    pointers in the FIRST POINTERS area must equal the HDR.NLST size
                                                    fields in the local pointers in the LAST POINTERS area. */
        uint64_t    reserved_36_39  :  4;   /**< Must be zero */
        uint64_t    addr            : 36;   /**< L2/DRAM byte pointer. Points to where the packet data starts.
                                                Ptr can be any byte alignment. Note that ptr is interpreted as a big-endian byte
                                                pointer when L is clear, a little-endian byte pointer when L is set. */
    } internal;
    struct
    {
        uint64_t    len0            : 16;   /**< Length of PCI / PCIe memory for address 0 */
        uint64_t    len1            : 16;   /**< Length of PCI / PCIe memory for address 1 */
        uint64_t    len2            : 16;   /**< Length of PCI / PCIe memory for address 2 */
        uint64_t    len3            : 16;   /**< Length of PCI / PCIe memory for address 3 */
    } pcie_length;
} cvmx_dma_engine_buffer_t;

/**
 * Initialize the DMA engines for use
 *
 * @return Zero on success, negative on failure
 */
int cvmx_dma_engine_initialize(void);

/**
 * Shutdown all DMA engines. The engeines must be idle when this
 * function is called.
 *
 * @return Zero on success, negative on failure
 */
int cvmx_dma_engine_shutdown(void);

/**
 * Return the number of DMA engimes supported by this chip
 *
 * @return Number of DMA engines
 */
int cvmx_dma_engine_get_num(void);

/**
 * Submit a series of DMA comamnd to the DMA engines.
 *
 * @param engine  Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
 * @param header  Command header
 * @param num_buffers
 *                The number of data pointers
 * @param buffers Comamnd data pointers
 *
 * @return Zero on success, negative on failure
 */
int cvmx_dma_engine_submit(int engine, cvmx_dma_engine_header_t header, int num_buffers, cvmx_dma_engine_buffer_t buffers[]);

/**
 * Build the first and last pointers based on a DMA engine header
 * and submit them to the engine. The purpose of this function is
 * to simplify the building of DMA engine commands by automatically
 * converting a simple address and size into the apropriate internal
 * or PCI / PCIe address list. This function does not support gather lists,
 * so you will need to build your own lists in that case.
 *
 * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
 * @param header DMA Command header. Note that the nfst and nlst fields do not
 *               need to be filled in. All other fields must be set properly.
 * @param first_address
 *               Address to use for the first pointers. In the case of INTERNAL,
 *               INBOUND, and OUTBOUND this is an Octeon memory address. In the
 *               case of EXTERNAL, this is the source PCI / PCIe address.
 * @param last_address
 *               Address to use for the last pointers. In the case of EXTERNAL,
 *               INBOUND, and OUTBOUND this is a PCI / PCIe address. In the
 *               case of INTERNAL, this is the Octeon memory destination address.
 * @param size   Size of the transfer to perform.
 *
 * @return Zero on success, negative on failure
 */
int cvmx_dma_engine_transfer(int engine, cvmx_dma_engine_header_t header,
                             uint64_t first_address, uint64_t last_address,
                             int size);

/**
 * Simplified interface to the DMA engines to emulate memcpy()
 *
 * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
 * @param dest   Pointer to the destination memory. cvmx_ptr_to_phys() will be
 *               used to turn this into a physical address. It cannot be a local
 *               or CVMX_SHARED block.
 * @param source Pointer to the source memory.
 *               cvmx_ptr_to_phys() will be used to turn this
 *               into a physical address. It cannot be a local
 *               or CVMX_SHARED block.
 * @param length Number of bytes to copy
 *
 * @return Zero on success, negative on failure
 */
static inline int cvmx_dma_engine_memcpy(int engine, void *dest, void *source, int length)
{
    cvmx_dma_engine_header_t header;
    header.u64 = 0;
    header.s.type = CVMX_DMA_ENGINE_TRANSFER_INTERNAL;
    return cvmx_dma_engine_transfer(engine, header, cvmx_ptr_to_phys(source),
                                    cvmx_ptr_to_phys(dest), length);
}

#ifdef	__cplusplus
}
#endif

#endif // __CVMX_CMD_QUEUE_H__
