| /********************************************************************* |
| * |
| * (C) Copyright IBM Corp. 2007,2010 |
| * |
| * 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 (at your |
| * option) 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, see <http://www.gnu.org/licenses>. |
| * |
| ********************************************************************/ |
| |
| #ifndef _DMA_INJFIFO_H_ /* Prevent multiple inclusion */ |
| #define _DMA_INJFIFO_H_ |
| |
| |
| /*! |
| * \file spi/DMA_InjFifo.h |
| * |
| * \brief DMA SPI Injection Fifo Definitions and Inline Functions |
| * |
| * This include file contains inline functions that are used to interface with |
| * BG/P DMA injection fifos at the lowest level. |
| * Functions include |
| * - initialize |
| * - get fifo start, head, tail, end, size, free space, descriptor count |
| * - set fifo head, tail, start PA, head PA, tail PA, end PA |
| * - increment tail |
| * - inject descriptor(s) |
| * - query status: not empty, available, threshold crossed, activated, |
| * descriptor done. |
| * - set status: clear threshold crossed, activate, deactivate |
| * |
| * Data structures are defined to manipulate the injection fifos: |
| * - An injection fifo group structure defining a group of injection fifos |
| * - Within the group are injection fifo structures |
| * - Within each injection fifo structure is a software fifo structure |
| * - Each software fifo structure points to its corresponding hardware |
| * fifo structure in the DMA SRAM |
| * |
| * \verbatim Picture of data structures: |
| |
| ========DDR MEMORY===================|==========DMA SRAM MEMORY========== |
| ------------------------------ | |
| | DMA_InjFifoGroup_t | | |
| | | | ----------------------------- |
| | status --------------------|-------|---->| DMA_InjFifoStatus_t | |
| | fifo[0..31] | | ----------------------------- |
| | ------------------------ | | |
| | | DMA_InjFifo_t | | | |
| | | | | | |
| | 0 | ------------------- | | | ----------------------------- |
| | | | DMA_Fifo_t |-|-|-------|---->| DMA_FifoHW_t | |
| | | ------------------- | | | ----------------------------- |
| | ------------------------ | | |
| | . | | |
| | . | | |
| | . | | |
| | ------------------------ | | |
| | | DMA_InjFifo_t | | | |
| | | | | | |
| |31 | ------------------- | | | ----------------------------- |
| | | | DMA_Fifo_t |-|-|-------|---->| DMA_FifoHW_t | |
| | | ------------------- | | | ----------------------------- |
| | ------------------------ | | |
| ------------------------------ | |
| |
| \endverbatim |
| * |
| * Definitions: |
| * - A fifo represents a contiguous block of DDR memory |
| * - A fifo has a starting address and an ending address (defines the memory |
| * block) |
| * - An injection fifo is a series of 32-byte descriptors. There is a count |
| * of the number of descriptors ever injected into this fifo. It will never |
| * wrap in the expected lifetime of a job. |
| * - Injection consists of copying a 32-byte descriptor into the next available |
| * slot (pointed to by the tail), incrementing the tail pointer, and |
| * incrementing the descriptor count for the fifo. |
| * - The DMA engine asynchronously processes descriptors, beginning with the |
| * descriptor pointed to by head, and ending with the descriptor just prior |
| * to tail. |
| * - There are injection (DMA InjFifo) and reception (DMA RecFifo) fifos |
| * (separate interfaces) |
| * - There are DMA_NUM_INJ_FIFO_GROUPS injection fifo groups |
| * - There are DMA_NUM_INJ_FIFOS_PER_GROUP injection fifos per group |
| * - Thus, there are DMA_NUM_INJ_FIFOS injection fifos per node |
| * - There are DMA_NUM_REC_FIFO_GROUPS reception fifo groups |
| * - There are DMA_NUM_REC_FIFOS_PER_GROUP reception fifos per group |
| * - Thus, there are DMA_NUM_REC_FIFOS reception fifos per node |
| * - A "shadow" refers to a copy of the elements of the fifo (start, end, head, |
| * tail) that is maintained by these inline functions. The shadows may be |
| * used to calculate other values such as free space. The shadows are updated |
| * by these inlines whenever the hardware fifo is read or written. |
| * |
| * \note These functions do not try to detect things that software shouldn't do, |
| * like injecting a descriptor into a remote_get fifo, since the hardware |
| * doesn't distinguish between remote get fifos and normal injection |
| * fifos. That sort of checking should be done in a higher level. |
| * |
| * \note Memory consistency/coherency inside these inlines is achieved using |
| * mbar and msync. |
| * |
| * MBAR is used to make sure that all writes to memory issued by the |
| * calling core have been accepted by the memory system before |
| * continuing. This guarantees that writes and reads to/from different |
| * addresses to go in defined order. |
| * |
| * MBAR EXAMPLE 1: When a store is done to DMA SRAM, it may not complete |
| * for a period of time. If a counter value is set, and then an injection |
| * fifo tail pointer is set, DMA may see the tail pointer update and begin |
| * the operation before the counter value has been set. Inserting an mbar |
| * between the setting of the counter and the setting of the tail pointer |
| * guarantees that the counter will be set before the tail pointer is |
| * updated. |
| * |
| * MBAR EXAMPLE 2: A counter hits zero. We process the hit-zero and write |
| * a "clear hit zero" to DMA SRAM, and then go read that counter's hit-zero |
| * status (different address). The hit-zero status will still indicate |
| * that it hit zero, even though we have already processed it, unless an |
| * mbar is inserted between clearing the hit-zero and reading the hit-zero |
| * status. |
| * |
| * MBAR PHILOSOPHY: After DMA SRAM is updated in the DMA inline functions, |
| * they always do at least an mbar (possibly an msync instead...see below). |
| * |
| * MSYNC does what mbar does, plus ensures consistency across cores. That |
| * is, it waits for snoops (invalidations of L1 cache) on the other cores |
| * to complete before continuing. This guarantees that all of the cores |
| * will see a consistent view of memory after the msync. |
| * |
| * MSYNC EXAMPLE: When a reception counter has hit zero, we assume the |
| * DMA'd data is available to be read by any core. However, old copies of |
| * that data may still be in the L1 caches. Inserting an msync after |
| * detecting that a counter has hit zero guarantees that the old data has |
| * been removed from the L1 caches. |
| * |
| * MSYNC PHILOSOPHY: After the inline functions detect that a counter has |
| * hit zero, they always do an msync. |
| * |
| * SPECULATIVE EXECUTION OF MSYNC: There are cases where msync is done |
| * conditionally. The CPU will begin execution of both sides of the |
| * condition before the result of the condition has been determined. |
| * Then, it will cancel the execution of one side once the result of the |
| * condition has been determined. This speculation is unwanted when |
| * the first instruction on one side of the condition is msync because |
| * cancelling an msync is similar to executing the complete msync. |
| * To avoid this speculative execution of msync, we call |
| * _bgp_msync_nonspeculative(). This will trick the CPU so it won't begin |
| * the msync until the result of the condition is known. |
| * |
| * CALLER ADVICE: Users of these functions should not need to do |
| * mbar/msync themselves, unless they are doing something like the |
| * following: Read a counter and operate on the result when the counter |
| * hasn't reached zero. The caller will need to perform an msync after |
| * reading the counter in order to ensure that snoops have completed |
| * on all CPUs before operating on the DMA'd data. |
| * |
| * \note General discussion on injection fifo interrupts. Both the warning |
| * threshold crossed and full fifo interrupts... |
| * |
| * For remote gets, a fifo is considered available if it has at least 512 bytes |
| * free (32 16B quads). An arriving remote get can be written if there are 512 |
| * bytes free, but after that the available goes low and no further remote gets |
| * can be written to any fifo. Furthermore, if any injection fifo has less than |
| * 512 bytes free, the fifo becomes unavailable and any arriving remote get |
| * packet will cause an interrupt to fire and the rDMA will stop. |
| * |
| * Specifically, if an injection fifo has less than 512 B (by either injecting |
| * or remote gets) the iDMA will continue to operate and the rDMA will continue |
| * to operate until any remote get packet arrives to any fifo, at which point |
| * an interrupt fires and the rDMA stops. |
| * |
| * Note that these interrupts were put in for warnings of remote get fifos |
| * becoming nearly full. However the time between when the warning fires and the |
| * condition is cleared may be long, reconfiguring an almost full remote get |
| * fifo is difficult, and recovery from full remote get injection fifos is very |
| * difficult. Since software can prevent this, and since recovery is so |
| * difficult, we consider injection fifo threshold crossing interrupts and |
| * injection fifo full interrupts to be fatal. Thus there is no handler function |
| * in the injection fifo allocation routine. |
| * |
| * So software needs to manage injection and remote get fifo space so that there |
| * are always at least 512 bytes of free space in every fifo. To accomplish |
| * this, software needs to guarantee it won't inject descriptors if doing so |
| * would trigger an interrupt or make the fifo unavailable. |
| * |
| * This can be done by setting the interrupt threshold to 0 (interrupt fires if |
| * free space <= threshhold), and not injecting if after injection there are |
| * less than DMA_MIN_INJECT_SIZE_IN_QUADS (=32) slots. Furthermore, remote |
| * get space should not be allocated if doing so might result in strictly less |
| * than DMA_MIN_INJECT_SIZE_IN_QUADS slots. |
| * |
| */ |
| |
| |
| |
| #include <common/namespace.h> |
| /* #include <memory.h> */ |
| |
| |
| __BEGIN_DECLS |
| |
| |
| /*! |
| * \brief __INLINE__ definition |
| * |
| * Option 1: |
| * Make all functions be "static inline": |
| * - They are inlined if the compiler can do it |
| * - If the compiler does not inline it, a single copy of the function is |
| * placed in the translation unit (eg. xxx.c)for use within that unit. |
| * The function is not externalized for use by another unit...we want this |
| * so we don't end up with multiple units exporting the same function, |
| * which would result in linker errors. |
| * |
| * Option 2: |
| * A GNU C model: Use "extern inline" in a common header (this one) and provide |
| * a definition in a .c file somewhere, perhaps using macros to ensure that the |
| * same code is used in each case. For instance, in the header file: |
| * |
| \verbatim |
| #ifndef INLINE |
| # define INLINE extern inline |
| #endif |
| INLINE int max(int a, int b) { |
| return a > b ? a : b; |
| } |
| \endverbatim |
| * |
| * ...and in exactly one source file (in runtime/SPI), that is included in a |
| * library... |
| * |
| \verbatim |
| #define INLINE |
| #include "header.h" |
| \endverbatim |
| * |
| * This allows inlining, where possible, but when not possible, only one |
| * instance of the function is in storage (in the library). |
| */ |
| #ifndef __INLINE__ |
| #define __INLINE__ extern inline |
| #endif |
| |
| |
| |
| #include <spi/DMA_Assert.h> |
| #include <spi/DMA_Fifo.h> |
| #include <spi/DMA_Descriptors.h> |
| |
| /* |
| * You can save a few cycles by using the parallel floating point unit to do the 'memcpy' |
| * as part of injecting a descriptor into a FIFO; but you then need to quadword-align the source memory |
| * and you may need to save/restore the FP context. Setting k_use_fp_to_inject to 0 arranges for the |
| * generated code to use integer registers for the 'memcpy'. |
| */ |
| enum { |
| k_use_fp_to_inject = 0 |
| }; |
| |
| |
| /*! |
| * \brief Number of Injection Fifo Groups |
| */ |
| #define DMA_NUM_INJ_FIFO_GROUPS 4 |
| |
| |
| /*! |
| * \brief Number of Injection Fifos per Group |
| */ |
| #define DMA_NUM_INJ_FIFOS_PER_GROUP 32 |
| |
| |
| /*! |
| * \brief Number of Injection Fifos (total) |
| */ |
| #define DMA_NUM_INJ_FIFOS (DMA_NUM_INJ_FIFO_GROUPS*DMA_NUM_INJ_FIFOS_PER_GROUP) |
| |
| |
| /*! |
| * \brief Minimum Free Space Required After Injection |
| * |
| * This is the number of 16-byte quads that need to be free in a fifo after |
| * injection of a descriptor. |
| */ |
| #define DMA_MIN_INJECT_SIZE_IN_QUADS 32 |
| |
| |
| /*! |
| * \brief Number of 16-byte quads in a fifo descriptor |
| * |
| */ |
| #define DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS 2 |
| |
| |
| /*! |
| * \brief Number of bytes in a fifo descriptor |
| * |
| */ |
| #define DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS*16 |
| |
| |
| /*! |
| * \brief Minimum size of a fifo, somewhat arbitrary |
| */ |
| #define DMA_MIN_INJ_FIFO_SIZE_IN_BYTES (256*4) |
| |
| |
| /*! |
| * \brief Injection DMA Fifo Structure |
| * |
| * This structure contains a software DMA fifo structure (defined in DMA_Fifo.h) |
| * and other fields that are specific to an injection fifo used by software. |
| * |
| * \todo Some more careful thought should be given how to group these so as to |
| * get best memory system performance. |
| * eg. Probably want to ALIGN_L3_CACHE the fifo_hw_ptr. |
| * |
| */ |
| typedef struct DMA_InjFifo_t |
| { |
| DMA_Fifo_t dma_fifo; /*!< Common software fifo structure */ |
| unsigned short int fifo_id; /*!< The fifo identifier (0 to |
| DMA_NUM_INJ_FIFOS_PER_GROUP-1). */ |
| |
| unsigned long long desc_count; /*!< The number of descriptors that have |
| ever been injected into this fifo. */ |
| |
| unsigned int occupiedSize; /*!< The number of 16B quads in the fifo that |
| are logically occupied. This does not |
| include the DMA_MIN_INJECT_SIZE_IN_QUADS |
| that always remains logically occupied. */ |
| /*! |
| * \note The following fields contain info about the fifo that affects the |
| * DCR values configuring the fifo. |
| */ |
| unsigned short int priority; /*!< 0 = Normal priority, 1 = High priority. |
| The DMA uses this to determine which |
| injection fifo to serve next. |
| Reflected in DCR addresses |
| _BGP_DCR_iDMA_FIFO_PRIORITY(i), where i |
| is the group_id. 0xD32 - 0xD35. |
| Fifo j is high priority if bit j in the |
| DCR is 1, otherwise it is normal |
| priority. */ |
| |
| unsigned short int local; /*!< 0 = non-local, 1 = local. |
| If 0, this fifo uses the torus and |
| ts_inj_map must be non-zero. |
| If 1, this fifo is used for tranfsers |
| local to the node only. |
| Reflected in DCR addresses |
| _BGP_DCR_iDMA_LOCAL_COPY(i), where i |
| is the group_id. 0xD5C - 0xD5F. |
| Fifo j is for local transfers if bit j |
| in the DCR is 1, otherwise it is for |
| torus transfers. */ |
| |
| unsigned char ts_inj_map; /*!< 8 bit vector mask indicating which torus |
| fifos can be used by this DMA fifo. |
| Reflected in DCR addresses |
| _BGP_DCR_iDMA_TS_INJ_FIFO_MAP(k) where k |
| is the fifo_id. 0xD3C - 0xD5B. |
| Fifo k can inject in torus fifo j if |
| bit j of the k'th DCR byte is 1. */ |
| } |
| DMA_InjFifo_t; |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Status structure |
| * |
| * This structure maps the DMA SRAM for a particular group of |
| * DMA_NUM_INJ_FIFOS_PER_GROUP fifos. |
| * |
| */ |
| typedef struct DMA_InjFifoStatus_t |
| { |
| volatile unsigned not_empty; /*!< R bitmask, 1 bit/fifo: |
| Injection FIFO not empty. */ |
| |
| volatile unsigned reserved_0; /*!< HOLE */ |
| |
| volatile unsigned available; /*!< R bitmask, 1 bit/fifo: |
| Injection FIFO available. */ |
| |
| volatile unsigned reserved_1; /*!< HOLE */ |
| |
| volatile unsigned threshold_crossed; /*!< R bitmask, 1 bit/fifo: |
| Threshold crossed. */ |
| |
| volatile unsigned reserved_2; /*!< HOLE */ |
| |
| volatile unsigned clear_threshold_crossed;/*!< W bitmask, 1 bit/fifo: |
| Clear threshold crossed. */ |
| |
| volatile unsigned reserved_3; /*!< HOLE */ |
| |
| volatile unsigned activated; /*!< R bitmask, 1 bit/fifo: |
| Retrieve activated fifos. */ |
| |
| volatile unsigned activate; /*!< W bitmask, 1 bit/fifo: |
| Set "1" to activate fifo. */ |
| |
| volatile unsigned deactivate; /*!< W bitmask, 1 bit/fifo: |
| Set "1" to deactivate fifo*/ |
| } |
| DMA_InjFifoStatus_t; |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Group Structure |
| * |
| * This structure defines a DMA InjFifo Group. It points to a |
| * DMA InjFifo Status structure, and contains DMA_NUM_INJ_FIFOS_PER_GROUP |
| * DMA InjFifo structures. |
| * |
| * It is passed into the DMA_InjFifoGroupAllocate system call. |
| * The system call sets up the requested fifos, and fills in this fifo group |
| * structure, including the appropriate DMA InjFifo structures within it. |
| * |
| * It also contains permission bits to use the fifos, one bit per fifo. |
| * When the permission bit is on, the corresponding fifo belongs to this |
| * group and can be used. Otherwise, the fifo should not be used as part |
| * of this group. These permission bits are used as follows: |
| * 1. Inline functions will ASSERT when an attempt is made |
| * to use a fifo that is not part of this group. |
| * 2. Inline functions will use the permission bits as a mask |
| * to return status information only for fifos that are allocated |
| * to this group. |
| * |
| */ |
| typedef struct DMA_InjFifoGroup_t |
| { |
| DMA_InjFifoStatus_t *status_ptr; /*!< Pointer to fifo status. */ |
| |
| DMA_InjFifo_t fifos[DMA_NUM_INJ_FIFOS_PER_GROUP];/*!< Array |
| of fifo structures. The i-th struct |
| is defined and usable only if |
| bit i of permissions = 1. */ |
| |
| unsigned int permissions; /*!< Permissions bit vector. Bit i is 1 |
| if permitted to use fifo i. The fifo |
| is allocated to this group. */ |
| |
| unsigned int group_id; /*!< The id of this group (0 to |
| DMA_NUM_INJ_FIFO_GROUPS-1). */ |
| } |
| DMA_InjFifoGroup_t; |
| |
| |
| /*! |
| * \brief Remote Get Fifo Full Handler Function Prototype |
| * |
| * A function with this signature receives control when one or more remote |
| * get fifos have filled. This function should do the following to help |
| * make space in the fifo(s): |
| * 1. Determine if there are any remote get fifos full or nearly full. |
| * 2. For each such fifo: |
| * 1. Allocate a larger fifo |
| * 2. Copy the descriptors from the old fifo to the new fifo |
| * 3. Call DMA_InjFifoInitById() to register the new fifo with the DMA |
| * 4. Call DMA_InjFifoSetTailById() to set the new fifo's tail pointer |
| * 5. Free the old fifo |
| * |
| * A function of this type can be registered on DMA_InjFifoGroupAllocate(). |
| * |
| * \param[in] fg_ptr Pointer to the fifo group associated with this fifo. |
| * \param[in] f_num The fifo number that has filled. This is |
| * relative to the DMA fifo group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] handler_param An opaque pointer provided by the caller who |
| * registered this handler. |
| */ |
| typedef void (*DMA_InjFifoRgetFifoFullHandler_t)( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int f_num, |
| void *handler_parm |
| ); |
| |
| |
| /*! |
| * \brief Remote Get Fifo Full Handler Table Entry |
| * |
| * This defines an entry in the Remote Get Fifo Full Handler Table. |
| * It identifies the fifo group pointer associated with the full fifo, |
| * and the pointer to the handler function to receive control to handle |
| * the fifo full condition and the opaque pointer to be passed to the |
| * handler function when it is called. The core number of the core that |
| * will process the condition is associated with each entry. |
| */ |
| typedef struct DMA_InjFifoRgetFifoFullHandlerEntry_t |
| { |
| DMA_InjFifoGroup_t *fg_ptr; /*!< Pointer to injection fifo group */ |
| DMA_InjFifoRgetFifoFullHandler_t handler; /*!< Pointer to handler function */ |
| void *handler_parm; /*!< Pointer to be passed to |
| the handler. */ |
| uint32_t core_num;/*!< Core number of the core that |
| will process the condition. */ |
| } DMA_InjFifoRgetFifoFullHandlerEntry_t; |
| |
| |
| /*! |
| * |
| * \brief Remote Get Fifo Full Handler Table |
| * |
| * An array of entries, one per injection fifo. Each entry specifies the fifo |
| * group structure and the handler function that will receive control to |
| * handle a remote get fifo full condition for fifos in that fifo group. |
| */ |
| extern DMA_InjFifoRgetFifoFullHandlerEntry_t DMA_RgetFifoFullHandlerTable[DMA_NUM_INJ_FIFOS]; |
| |
| |
| /*! |
| * \brief Remote Get Fifo Full Init Has Been Done Indicator |
| * |
| * 0 means the initialization has not been done. |
| * 1 means the initialization has been done. |
| */ |
| extern int DMA_InjFifoRgetFifoFullInitHasBeenDone; |
| |
| |
| /*! |
| * \brief Remote Get Fifo Full Initialization |
| * |
| * Initialize data structures and interrupt handlers to handle a remote get |
| * fifo full condition. |
| * |
| * \param[in] interruptGroup The handle that identifies the remote get fifo |
| * full interrupts (only one interrupt, in this |
| * case, group 3, irq 24). |
| * \param[in] rget_barrier A function pointer to a function that implments |
| * the barrier that is used by the handler function |
| * to synchronize all cores in the node as they |
| * each handle the interrupt (it is a broadcasted |
| * interrupt). |
| * \param[in] rget_barrier_arg The generic arg to pass to the barrier function. |
| */ |
| void DMA_InjFifoRgetFifoFullInit( Kernel_InterruptGroup_t interruptGroup, |
| void (*rget_barrier)(void *), |
| void *rget_barrier_arg ); |
| |
| |
| /*! |
| * \brief Query Free DMA InjFifos within a Group |
| * |
| * This function is a wrapper around a system call that returns a list of the |
| * free (available to be allocated) fifos within the specified group. |
| * |
| * \param[in] grp Group number being queried |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1) |
| * \param[out] num_fifos Pointer to an int where the number of free |
| * fifos in the specified group is returned |
| * \param[out] fifo_ids Pointer to an array of num_fifos short ints where |
| * the list of free fifos is returned. |
| * Each short int is the fifo number |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * The caller must provide space for |
| * DMA_NUM_INJ_FIFOS_PER_GROUP ints, |
| * in case the entire fifo group is free. |
| * |
| * \retval 0 Successful. num_fifos and fifo_ids array set as described. |
| * \retval -1 Unsuccessful. errno gives the reason. |
| * |
| */ |
| __INLINE__ int DMA_InjFifoGroupQueryFree( |
| int grp, |
| int *num_fifos, |
| int *fifo_ids |
| ) |
| { |
| return Kernel_InjFifoGroupQueryFree( grp, |
| (uint32_t*)num_fifos, |
| (uint32_t*)fifo_ids); |
| } |
| |
| |
| /*! |
| * \brief Allocate DMA InjFifos From A Group |
| * |
| * This function is a wrapper around a system call that allocates specified |
| * DMA injection fifos from the specified group. Parameters specify whether |
| * each fifo is high or normal priority, local or non-local, and which torus |
| * fifos it maps to. A DMA_InjFifoGroup_t structure is returned for |
| * use in other inline functions to operate on the allocated fifos. |
| * |
| * Refer to the interrupt discussion at the top of this include file to see why |
| * there are no interrupt-related parameters. |
| * |
| * \param[in] grp Group number whose DMA injection fifos are being |
| * allocated (0 to DMA_NUM_INJ_FIFO_GROUPS-1) |
| * \param[in] num_fifos Number of fifos to be allocated from the group |
| * (1 to DMA_NUM_INJ_FIFOS_PER_GROUP) |
| * \param[in] fifo_ids Pointer to an array of num_fifos ints where |
| * the list of fifos to be allocated is provided. |
| * Each int is the fifo number |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] priorities Pointer to an array of num_fifos short ints where |
| * the list of priorities to be assigned to the fifos |
| * is provided. Each short int indicates the priority |
| * to be assigned to each of the fifos identified in |
| * the fifo_ids array (0 is normal, 1 is high priority). |
| * \param[in] locals Pointer to an array of num_fifos short ints where |
| * an indication is provided of whether each fifo will |
| * be used for local transfers (within the same node) |
| * or torus transfers. Each short int indicates the |
| * local/non-local attribute to be assigned to each of |
| * the fifos identified in the fifo_ids array (0 is |
| * non-local, 1 is local). If 0, the corresponding |
| * array element in ts_inj_maps indicates which torus |
| * fifos can be injected. |
| * \param[in] ts_inj_maps Pointer to an array of num_fifos chars where |
| * the torus fifos that can be injected are specified |
| * for each fifo. Each char specifies which of |
| * the 8 torus injection fifos can be injected when a |
| * descriptor is injected into the DMA injection fifo. |
| * Must be non-zero when the corresponding "locals" |
| * is 0. |
| * Bits 0-3 are for torus group 0. |
| * Bits 4-7 are for torus group 1. |
| * Bits 3 and 7 are the high priority fifos. |
| * \param[in] rget_handler Pointer to a function with prototype |
| * DMA_InjFifoRgetFifoFullHandler_t that will handle |
| * a remote get fifo full condition for fifos in this |
| * fifo group. If NULL is specified, the condition |
| * will not be handled. |
| * \param[in] rget_handler_parm A pointer to opaque storage that will be |
| * passed to the rget_handler. |
| * \param[in] rget_interruptGroup A InterruptGroup_t that identifies the |
| * group of interrupts that handle the remote get |
| * fifo full condition. It is only one interrupt: |
| * group 3, irq 24. |
| * \param[in] rget_barrier Function point to a function that implements |
| * a barrier that is used by the rget fifo full |
| * interrupt handler. This barrier should be across |
| * all cores of all active processes on this compute node. |
| * \param[in] rget_barrier_arg Generic arg to pass to barrier function. |
| * \param[out] fg_ptr Pointer to a structure that is filled in upon |
| * successful return for use in other inline functions |
| * to operate on the allocated fifos. |
| * \li fifos - Array of fifo structures. Structures |
| * for allocated fifos are initialized as |
| * documented below. Structures for |
| * fifos not allocated by this instance of |
| * this syscall are initialized to binary |
| * zeros. Allocated fifos are enabled. |
| * \li status_ptr - Points to status area within the |
| * DMA memory map. |
| * \li permissions - Bits indicating which fifos were |
| * allocated during this syscall. |
| * \li group_id - The id of this group. |
| * |
| * \retval 0 Successful. Fifos allocated and fg_ptr structure filled in as |
| * described. |
| * \retval -1 Unsuccessful. errno gives the reason. |
| * |
| * \return The group fifo structure pointed to by fg_ptr is completely |
| * initialized as follows: |
| * - status_ptr points to the appropriate fifo group DMA memory map |
| * - fifo structures array. Fifo structures for fifos not allocated |
| * during this syscall are initialized to binary zeros. Fifo |
| * structures for fifos allocated during this syscall are initialized: |
| * - fifo_hw_ptr points to the DMA memory map for this fifo. The |
| * hardware start, end, head, and tail are set to zero by the |
| * kernel. |
| * - All other fields in the structure are set to zero by the kernel |
| * except priority, local, and ts_inj_map are set to reflect what |
| * was requested in the priorities, locals, and ts_inj_maps |
| * syscall parameters. |
| * |
| */ |
| __INLINE__ int DMA_InjFifoGroupAllocate( |
| int grp, |
| int num_fifos, |
| int *fifo_ids, |
| unsigned short int *priorities, |
| unsigned short int *locals, |
| unsigned char *ts_inj_maps, |
| DMA_InjFifoRgetFifoFullHandler_t rget_handler, |
| void *rget_handler_parm, |
| Kernel_InterruptGroup_t rget_interruptGroup, |
| void (*rget_barrier)(void *), |
| void *rget_barrier_arg, |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| int rc; |
| int i, global_fifo_id; |
| |
| rc = Kernel_InjFifoGroupAllocate( grp, |
| num_fifos, |
| (uint32_t*)fifo_ids, |
| (uint16_t*)priorities, |
| (uint16_t*)locals, |
| (uint8_t*)ts_inj_maps, |
| (uint32_t*)fg_ptr); |
| |
| if ( rc == 0 ) |
| { |
| /* |
| * If a remote get fifo full handler has been provided, update the table |
| * to indicate that this handler will handle full conditions on the fifos |
| * just allocated. |
| */ |
| if ( rget_handler ) |
| { |
| /* |
| * If rget handler init has not been done, do it: |
| */ |
| if ( DMA_InjFifoRgetFifoFullInitHasBeenDone == 0 ) |
| DMA_InjFifoRgetFifoFullInit( rget_interruptGroup, |
| rget_barrier, |
| rget_barrier_arg ); |
| |
| for (i=0; i<num_fifos; i++) |
| { |
| global_fifo_id = (grp * DMA_NUM_INJ_FIFOS_PER_GROUP) + fifo_ids[i]; |
| DMA_RgetFifoFullHandlerTable[global_fifo_id].fg_ptr = fg_ptr; |
| DMA_RgetFifoFullHandlerTable[global_fifo_id].handler = rget_handler; |
| DMA_RgetFifoFullHandlerTable[global_fifo_id].handler_parm = |
| rget_handler_parm; |
| DMA_RgetFifoFullHandlerTable[global_fifo_id].core_num= |
| Kernel_PhysicalProcessorID(); |
| } |
| |
| /* |
| * Indicate done with initialization. |
| */ |
| DMA_InjFifoRgetFifoFullInitHasBeenDone = 1; |
| } |
| } |
| |
| return(rc); |
| } |
| |
| |
| /*! |
| * \brief Free DMA InjFifos From A Group |
| * |
| * This function is a wrapper around a system call that frees DMA injection |
| * counters from the specified group. |
| * |
| * \param[in] grp Group number whose DMA injection fifos are being |
| * freed (0 to DMA_NUM_INJ_FIFO_GROUPS-1) |
| * \param[in] num_fifos Number of fifos to be freed from the group |
| * (1 to DMA_NUM_INJ_FIFOS_PER_GROUP) |
| * \param[in] fifo_ids Pointer to an array of num_fifos ints where |
| * the list of fifos to be freed is provided. |
| * Each int is the fifo number (0 to num_fifos-1). |
| * \param[in] fg_ptr Pointer to the structure previously filled in when |
| * these fifos were allocated. Upon successful |
| * return, this structure is updated to reflect the |
| * freed fifos: |
| * \li fifos - Structures for freed fifos zero'd. |
| * Freed fifos are disabled. |
| * \li permissions - Bits cleared for each freed fifo. |
| * |
| * \retval 0 Successful. Fifos freed and fg_ptr structure updated as described. |
| * \retval -1 Unsuccessful. errno gives the reason. |
| * |
| * \note This is a fatal error if any of the fifos are non empty and activated |
| * |
| */ |
| __INLINE__ int DMA_InjFifoGroupFree( |
| int grp, |
| int num_fifos, |
| int *fifo_ids, |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| return Kernel_InjFifoGroupFree( grp, |
| num_fifos, |
| (uint32_t*)fifo_ids, |
| (uint32_t*)fg_ptr); |
| } |
| |
| |
| |
| |
| /* |
| * ----------------------------------------------------------------------------- |
| * Calls to access the Fifo, given a pointer to the injection fifo structure |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| |
| |
| |
| /*! |
| * \brief Set DMA Injection Fifo Head |
| * |
| * Set a DMA injection fifo's "head", given an injection fifo structure |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] va_head Virtual address of the head to be set |
| * |
| * \return None |
| * |
| * \post va_head is set in both the hardware and software fifo structures, |
| * and the fifo free space is recalculated. |
| * |
| * \note Normally, for an injection fifo, the dma manipulates the head, but in |
| * optimized persistant communications the core can do it if it is sure |
| * the fifo is empty at the time this is called. |
| */ |
| __INLINE__ void DMA_InjFifoSetHead( |
| DMA_InjFifo_t *f_ptr, |
| void *va_head |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| |
| DMA_FifoSetHead( &f_ptr->dma_fifo, |
| va_head ); |
| } |
| |
| |
| /*! |
| * \brief Increment DMA Injection Fifo Tail |
| * |
| * Increment a DMA injection fifo's "tail", given an injection fifo structure |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] incr The number of quads (16 byte units) to increment the |
| * tail pointer by. This value must be even (ie. descriptors |
| * are 32 bytes). |
| * |
| * \retval None |
| * |
| * \post va_tail is set in both the hardware and software fifo structures, |
| * the fifo free space is recalculated, and the fifo's descriptor count |
| * is incremented according to the incr. |
| * |
| * \note This function does not check if there is free space in the fifo |
| * for this many quads. It must be preceeded by a check of the |
| * free space. |
| */ |
| __INLINE__ void DMA_InjFifoIncrementTail( |
| DMA_InjFifo_t *f_ptr, |
| unsigned int incr |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| SPI_assert( (incr & 0x1) == 0 ); |
| |
| { |
| void *va_tail = DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ); |
| |
| void *va_end = DMA_FifoGetEndFromShadow( &f_ptr->dma_fifo ); |
| |
| unsigned int incr_bytes = incr << 4; |
| |
| unsigned int bytes_to_end = (unsigned)va_end - (unsigned)va_tail; |
| |
| /* |
| * Note: The following check must be >= instead of just >. We never want |
| * the tail to be equal to the end so we can always copy a descriptor |
| * to the tail, safely. |
| */ |
| if ( incr_bytes >= bytes_to_end ) |
| { |
| va_tail = (char *) |
| ( (unsigned)DMA_FifoGetStartFromShadow( &f_ptr->dma_fifo ) + |
| ( incr_bytes - bytes_to_end ) ); |
| } |
| else |
| { |
| va_tail = (char *)( (unsigned)va_tail + incr_bytes ); |
| } |
| |
| DMA_FifoSetTail( &f_ptr->dma_fifo, |
| va_tail ); |
| |
| f_ptr->desc_count += (incr >> 1); |
| } |
| |
| } |
| |
| |
| /*! |
| * \brief Get DMA Injection Fifo Descriptor Count |
| * |
| * Get a DMA injection fifo's "descriptor count", given an injection fifo |
| * structure |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * |
| * \retval desc_count The descriptor count for the specified fifo |
| * |
| */ |
| __INLINE__ unsigned long long DMA_InjFifoGetDescriptorCount( |
| DMA_InjFifo_t *f_ptr |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| |
| return f_ptr->desc_count; |
| } |
| |
| |
| /*! |
| * \brief Is DMA Descriptor Done |
| * |
| * Return whether a specified descriptor is still in the specified injection |
| * fifo (not done). The descriptor is identified by the descriptor count |
| * immediately after the descriptor was injected into the fifo (returned by |
| * DMA_InjFifoIncrementTail(). |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] desc_count The descriptor count immediately after the |
| * descriptor in question was injected into |
| * the fifo. |
| * \param[in] update 0 Do not update the fifo's shadow information. |
| * 1 Update the fifo's shadow information. |
| * It is a performance optimization to only update the |
| * shadow information once for a group of descriptors |
| * being processed. |
| * |
| * \retval 0 False. The descriptor identified by desc_count is not done. |
| * It is still in the fifo. |
| * \retval 1 True. The descriptor identified by desc_count is done. |
| * It is no longer in the fifo. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoIsDescriptorDone( |
| DMA_InjFifo_t *f_ptr, |
| unsigned long long desc_count, |
| unsigned int update |
| ) |
| { |
| unsigned long long num_desc_in_fifo; |
| unsigned int free_space; |
| DMA_Fifo_t *fifo_ptr; |
| |
| SPI_assert( f_ptr != NULL ); |
| |
| fifo_ptr = &(f_ptr->dma_fifo); |
| |
| /* If caller wants a fresh look in the fifo, update its free space. |
| * Otherwise, fetch the free space based on shadows. |
| */ |
| if (update) |
| free_space = DMA_FifoGetFreeSpace (fifo_ptr, 1, 0); |
| else |
| free_space = DMA_FifoGetFreeSpaceNoUpdateCalculation(fifo_ptr); |
| |
| /* Compute the desc_count of the oldest descriptor in the fifo (minus 1) |
| * Note: Each desc is a 32B unit and the below are 16B entities |
| */ |
| num_desc_in_fifo = ( DMA_FifoGetSize(fifo_ptr) - free_space ) / 2; |
| |
| /* Determine if the specified desc_count is still in the fifo. |
| * We take the current descriptor count for this fifo and subtract the |
| * number of descriptors still in the fifo. This is the descriptor count |
| * of the oldest descriptor still remaining in the fifo (minus 1). |
| * We compare that with the caller's desc_count to determine if the |
| * caller's descriptor is still in the fifo. |
| */ |
| if ( desc_count <= (DMA_InjFifoGetDescriptorCount(f_ptr) - num_desc_in_fifo) ) |
| return (1); /* Descriptor is done */ |
| else |
| return (0); /* Descriptor is not done */ |
| |
| } |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Reserve Descriptor Storage |
| * |
| * Reserve storage in a DMA injection fifo for a remote get descriptor, given |
| * an injection fifo structure. |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * |
| * \retval 0 Successful. There was enough space in the fifo and the |
| * storage was reserved. |
| * \retval -1 Unsuccessful. There was not enough space in the fifo. |
| * |
| * \note Internally, this increments the occupiedSize of the fifo by |
| * DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS. |
| * |
| */ |
| __INLINE__ int DMA_InjFifoReserveDescriptorStorage( |
| DMA_InjFifo_t *f_ptr |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| |
| if ( (DMA_FifoGetSize(&f_ptr->dma_fifo) - f_ptr->occupiedSize) >= |
| (DMA_MIN_INJECT_SIZE_IN_QUADS + DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS) ) { |
| f_ptr->occupiedSize += DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS; |
| return (0); |
| } |
| else { |
| return (-1); |
| } |
| } |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Free Descriptor Storage Reservation |
| * |
| * Free a reservation for storage for a remote get descriptor in a DMA injection |
| * fifo, previously reserved using DMA_InjFifoReserveDescriptorStorageById(). |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * |
| * \return None |
| * |
| * \note Internally, this decrements the occupiedSize of the fifo by |
| * DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS. |
| * |
| */ |
| __INLINE__ void DMA_InjFifoFreeDescriptorStorageReservation( |
| DMA_InjFifo_t *f_ptr |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| SPI_assert( f_ptr->occupiedSize >= DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ); |
| |
| f_ptr->occupiedSize -= DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS; |
| } |
| |
| |
| /*! |
| * \brief Check If An Injection Fifo Has Space For Injection |
| * |
| * Check if an injection fifo has enough space for a single descriptor to be |
| * injected. |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * |
| * \retval hasSpace An indicator of whether the fifo has space for a |
| * descriptor. |
| * - 0 (false) means the fifo is full. |
| * - 1 (true) means the fifo has space. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoHasSpace( |
| DMA_InjFifo_t *f_ptr |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| |
| { |
| unsigned int free_space; |
| |
| /* Get the free space in the fifo using the shadow value */ |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 0, /* Use shadow head */ |
| 0);/* use shadow tail */ |
| |
| /* |
| * If after injecting, (DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS is the amount we |
| * are going to inject), there is still at least the minimum allowable free |
| * space left in the fifo, go ahead and inject. We want at least |
| * DMA_MIN_INJECT_SIZE_IN_QUADS free space after injection. |
| * |
| * Otherwise, read the hardware head pointer and recalculate the free space, |
| * and check again. Note: We don't need to read the hardware tail |
| * pointer because only software updates that, and we recalculate the |
| * free space at that time. |
| * |
| * If there is still not enough room in the fifo, return 0, indicating that |
| * the descriptor could not be injected. |
| * |
| */ |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ) |
| { |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 1, /* Use hardware head */ |
| 0); /* Use shadow tail */ |
| |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ) return 0; |
| } |
| |
| return 1; /* There is space in the fifo. */ |
| } |
| } |
| |
| /* a 32-byte memcpy */ |
| static inline void DMA_DescriptorToFifo(char *store_ptr, const char *load_ptr) |
| { |
| int * store_int=(int *) store_ptr ; |
| int * load_int= (int *) load_ptr ; |
| int v0 = load_int[0] ; |
| int v1 = load_int[1] ; |
| store_int[0] = v0 ; |
| v0 = load_int[2] ; |
| store_int[1] = v1 ; |
| v1 = load_int[3] ; |
| store_int[2] = v0 ; |
| v0 = load_int[4] ; |
| store_int[3] = v1 ; |
| v1 = load_int[5] ; |
| store_int[4] = v0 ; |
| v0 = load_int[6] ; |
| store_int[5] = v1 ; |
| v1 = load_int[7] ; |
| store_int[6] = v0 ; |
| store_int[7] = v1 ; |
| } |
| /*! |
| * \brief Inject a Descriptor into a DMA Injection Fifo Without Checking for |
| * Space |
| * |
| * Inject a descriptor into a DMA injection fifo, given an injection fifo |
| * structure, without checking to see if there is enough space in the fifo. |
| * It is assumed that the caller has already checked for enough space using |
| * the DMA_InjFifoHasSpace() function. |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] desc A pointer to the descriptor to be injected. |
| * Must be 16-byte aligned. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - 1 means it was successfully injected. |
| * |
| * \see DMA_InjFifoHasSpace() |
| */ |
| __INLINE__ int DMA_InjFifoInjectDescriptorNoSpaceCheck( |
| DMA_InjFifo_t *f_ptr, |
| DMA_InjDescriptor_t *desc |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| SPI_assert( desc != NULL ); |
| |
| { |
| char *load_ptr, *store_ptr; |
| |
| /* |
| * Copy the descriptor to the current va_tail of the fifo. |
| * Msync to ensure the descriptor has been written to memory and the L1 caches |
| * are in sync. |
| * Move the tail past the descriptor so the DMA knows the descriptor is there. |
| * - handle wrapping |
| * - update free space |
| * |
| */ |
| |
| if( k_use_fp_to_inject) |
| { |
| if ( ( (unsigned)desc & 0xFFFFFFF0 ) == (unsigned)desc ) /* 16B aligned? */ |
| { |
| load_ptr = (char*)desc; |
| store_ptr = (char*)DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ); |
| _bgp_QuadLoad ( load_ptr, 0 ); |
| _bgp_QuadLoad ( load_ptr+16, 1 ); |
| _bgp_QuadStore( store_ptr, 0 ); |
| _bgp_QuadStore( store_ptr+16, 1 ); |
| } |
| else |
| { |
| memcpy( DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ), |
| desc, |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES ); |
| } |
| } |
| else |
| { |
| DMA_DescriptorToFifo((char*)DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ),(char*)desc) ; |
| } |
| |
| /* _bgp_msync(); mbar is good enough */ |
| _bgp_mbar(); |
| |
| DMA_InjFifoIncrementTail( f_ptr, |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ); |
| |
| return 1; /* Success */ |
| } |
| } |
| |
| |
| /*! |
| * \brief Inject a Descriptor into a DMA Injection Fifo |
| * |
| * Inject a descriptor into a DMA injection fifo, given an injection fifo |
| * structure |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] desc A pointer to the descriptor to be injected. |
| * Must be 16-byte aligned. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - 0 means it was not injected, most likely because |
| * the fifo is full. |
| * - 1 means it was successfully injected |
| * |
| * Caution: If you call this function two or more times in quick |
| * succession to try to put descriptors into a FIFO, occasionally |
| * one of the descriptors appears not to be acted on by the hardware. |
| * An alternative is to use DMA_InjFifoInjectDescriptors with a vector |
| * of descriptors; this appears to do the job reliably. |
| */ |
| __INLINE__ int DMA_InjFifoInjectDescriptor( |
| DMA_InjFifo_t *f_ptr, |
| DMA_InjDescriptor_t *desc |
| ) |
| { |
| SPI_assert( f_ptr != NULL ); |
| SPI_assert( desc != NULL ); |
| |
| { |
| unsigned int free_space; |
| char *load_ptr, *store_ptr; |
| |
| /* Get the free space in the fifo using the shadow value */ |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 0, /* Use shadow head */ |
| 0);/* use shadow tail */ |
| |
| /* |
| * If after injecting, (DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS is the amount we |
| * are going to inject), there is still at least the minimum allowable free |
| * space left in the fifo, go ahead and inject. We want at least |
| * DMA_MIN_INJECT_SIZE_IN_QUADS free space after injection. |
| * |
| * Otherwise, read the hardware head pointer and recalculate the free space, |
| * and check again. Note: We don't need to read the hardware tail |
| * pointer because only software updates that, and we recalculate the |
| * free space at that time. |
| * |
| * If there is still not enough room in the fifo, return 0, indicating that |
| * the descriptor could not be injected. |
| * |
| */ |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ) |
| { |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 1, /* Use hardware head */ |
| 0); /* Use shadow tail */ |
| |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ) return 0; |
| } |
| |
| /* |
| * We have enough room in the fifo. |
| * Copy the descriptor to the current va_tail of the fifo. |
| * Msync to ensure the descriptor has been written to memory and the L1 caches |
| * are in sync. |
| * Move the tail past the descriptor so the DMA knows the descriptor is there. |
| * - handle wrapping |
| * - update free space |
| * |
| */ |
| |
| if( k_use_fp_to_inject) |
| { |
| if ( ( (unsigned)desc & 0xFFFFFFF0 ) == (unsigned)desc ) /* 16B aligned? */ |
| { |
| load_ptr = (char*)desc; |
| store_ptr = (char*)DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ); |
| _bgp_QuadLoad ( load_ptr, 0 ); |
| _bgp_QuadLoad ( load_ptr+16, 1 ); |
| _bgp_QuadStore( store_ptr, 0 ); |
| _bgp_QuadStore( store_ptr+16, 1 ); |
| } |
| else |
| { |
| memcpy( DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ), |
| desc, |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES ); |
| } |
| } |
| else |
| { |
| DMA_DescriptorToFifo((char*)DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ),(char*)desc) ; |
| } |
| |
| /* _bgp_msync(); mbar is good enough */ |
| _bgp_mbar(); |
| |
| DMA_InjFifoIncrementTail( f_ptr, |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ); |
| |
| return 1; /* Success */ |
| } |
| } |
| |
| |
| /*! |
| * \brief Inject Multiple Descriptors into a DMA Injection Fifo |
| * |
| * Inject multiple descriptors into a DMA injection fifo, given an injection fifo |
| * structure |
| * |
| * \param[in] f_ptr Pointer to the injection fifo structure |
| * \param[in] num_desc Number of descriptors to be injected |
| * \param[in] desc A pointer to an array of pointers to descriptors to be |
| * injected. The descriptors must be 16-byte aligned. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - less than num_desc means some were not injected, |
| * most likely because the fifo is full. |
| * - num_desc means all were successfully injected |
| * |
| */ |
| #if 0 |
| __INLINE__ int DMA_InjFifoInjectDescriptors( |
| DMA_InjFifo_t *f_ptr, |
| int num_desc, |
| DMA_InjDescriptor_t **desc |
| ) |
| { |
| int i; |
| int rc=0 ; |
| for(i=0;i<num_desc;i+=1) |
| { |
| int rc0=DMA_InjFifoInjectDescriptor(f_ptr,desc[i]) ; |
| rc += rc0 ; |
| } |
| return rc ; |
| } |
| #else |
| __INLINE__ int DMA_InjFifoInjectDescriptors( |
| DMA_InjFifo_t *f_ptr, |
| int num_desc, |
| DMA_InjDescriptor_t **desc |
| ) |
| { |
| unsigned int free_space; |
| unsigned int total_space_needed_in_quads; |
| void *va_tail; |
| void *va_end; |
| void *va_start; |
| char *target; |
| unsigned int num_quads_to_inject, num_quads_remaining; |
| int i; |
| char *load_ptr, *store_ptr; |
| |
| SPI_assert( f_ptr != NULL ); |
| SPI_assert( desc != NULL ); |
| SPI_assert( num_desc > 0 ); |
| |
| /* Get the free space in the fifo using the shadow value */ |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 0, /* Use shadow head */ |
| 0);/* Use shadow tail */ |
| |
| total_space_needed_in_quads = num_desc * |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS; |
| |
| /* |
| * If after injecting all descriptors (DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS |
| * per descriptor is the amount we are going to inject), there is still at |
| * least the minimum allowable free space left in the fifo, go ahead and |
| * inject. We want at least DMA_MIN_INJECT_SIZE_IN_QUADS free space |
| * after injection. |
| * |
| * Otherwise, read the hardware head pointer and recalculate the free space, |
| * and check again. |
| * |
| * If there is still not enough room in the fifo for any descriptors, |
| * return 0. Otherwise, continue and inject as many descriptors as possible. |
| * |
| */ |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| total_space_needed_in_quads ) |
| { |
| free_space = DMA_FifoGetFreeSpace( &f_ptr->dma_fifo, |
| 1, /* Use hardware head */ |
| 0); /* Use shadow tail */ |
| |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS ) return 0; |
| } |
| |
| /* |
| * We have enough room in the fifo for at least some descriptors. |
| * Copy the descriptors (as many as will fit) to the current va_tail of the |
| * fifo. |
| * Msync to ensure the descriptor has been written to memory and the L1 caches |
| * are in sync. |
| * Move the tail past the descriptor so the DMA knows the descriptor is there. |
| * - handle wrapping |
| * - update free space |
| * |
| */ |
| va_tail = DMA_FifoGetTailFromShadow( &f_ptr->dma_fifo ); |
| va_start = DMA_FifoGetStartFromShadow( &f_ptr->dma_fifo ); |
| va_end = DMA_FifoGetEndFromShadow( &f_ptr->dma_fifo ); |
| target = (char*)va_tail; |
| |
| if ( free_space < DMA_MIN_INJECT_SIZE_IN_QUADS + total_space_needed_in_quads ) { |
| num_quads_to_inject = free_space - DMA_MIN_INJECT_SIZE_IN_QUADS; |
| } |
| else { |
| num_quads_to_inject = total_space_needed_in_quads; |
| } |
| num_quads_remaining = num_quads_to_inject; |
| i = 0; |
| |
| while ( num_quads_remaining > 0 ) |
| { |
| SPI_assert( desc[i] != NULL ); |
| |
| if( k_use_fp_to_inject) |
| { |
| if ( ( (unsigned)desc[i] & 0xFFFFFFF0 ) == (unsigned)desc[i] ) /* 16B aligned? */ |
| { |
| load_ptr = (char*)desc[i]; |
| store_ptr = (char*)target; |
| _bgp_QuadLoad ( load_ptr, 0 ); |
| _bgp_QuadLoad ( load_ptr+16, 1 ); |
| _bgp_QuadStore( store_ptr, 0 ); |
| _bgp_QuadStore( store_ptr+16, 1 ); |
| } |
| else |
| { |
| memcpy( (char*)target, |
| desc[i], |
| DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES ); |
| } |
| } |
| else |
| { |
| DMA_DescriptorToFifo(target,(char*)(desc[i])) ; |
| } |
| |
| i++; |
| num_quads_remaining -= DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS; |
| target += DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES; |
| if ( target >= (char*)va_end ) |
| target = (char*)va_start; |
| } |
| |
| /* _bgp_msync(); mbar good enough */ |
| _bgp_mbar(); |
| |
| DMA_InjFifoIncrementTail( f_ptr, |
| num_quads_to_inject ); |
| |
| return i; /* Success */ |
| |
| } |
| #endif |
| |
| /*! |
| * \brief Get DMA Injection Fifo Group Number |
| * |
| * Get the DMA Injection Fifo Group number, given an injection fifo group |
| * structure. |
| * |
| * \param[in] fg_ptr Pointer to the structure previously filled in when the |
| * group was allocated. |
| * |
| * \return The DMA Injection Fifo Group number |
| * |
| */ |
| __INLINE__ int DMA_InjFifoGetGroupNum( |
| const DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| |
| return fg_ptr->group_id; |
| } |
| |
| |
| /*! |
| * \brief Get the "Not Empty" Status of an Injection Fifo Group |
| * |
| * Get the "Not Empty" status of the fifos that the specified fifo group has |
| * permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * |
| * \retval notEmptyStatus A 32-bit value, one bit per fifo. |
| * Bit i is 1 if the specified fifo group has |
| * permission to use fifo i and fifo i is not |
| * empty. |
| * Bit i is 0 if the specified fifo group either |
| * does not have permission to use fifo i, or fifo i |
| * is empty. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetNotEmpty( |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| |
| return ( fg_ptr->status_ptr->not_empty & fg_ptr->permissions ); |
| |
| } |
| |
| |
| /*! |
| * \brief Get the "available" Status of an Injection Fifo Group |
| * |
| * Get the "available" status of the fifos that the specified fifo group has |
| * permission to use. "available" means that the fifo is enabled and |
| * activated. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * |
| * \retval availableStatus A 32-bit value, one bit per fifo. |
| * Bit i is 1 if the specified fifo group has |
| * permission to use fifo i and fifo i is available |
| * Bit i is 0 if the specified fifo group either |
| * does not have permission to use fifo i, or fifo i |
| * is not available. |
| * |
| * \note Normally, there should be a 1 in every position except those that |
| * the specified fifo group does not have permission to use. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetAvailable( |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| |
| return ( fg_ptr->status_ptr->available & fg_ptr->permissions ); |
| |
| } |
| |
| |
| /*! |
| * \brief Get the "threshold crossed" Status of an Injection Fifo Group |
| * |
| * Get the "threshold crossed" status of the fifos that the specified fifo |
| * group has permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * |
| * \retval thresholdCrossedStatus A 32-bit value, one bit per fifo. |
| * Bit i is 1 if the specified fifo group has |
| * permission to use fifo i and fifo i has crossed |
| * a threshold. |
| * Bit i is 0 if the specified fifo group either |
| * does not have permission to use fifo i, or fifo i |
| * has not crossed a threshold. |
| * |
| * \note Normally, there should be a 0 in every position. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetThresholdCrossed( |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| |
| return ( fg_ptr->status_ptr->threshold_crossed & fg_ptr->permissions ); |
| |
| } |
| |
| |
| /*! |
| * \brief Set the "clear threshold crossed" Status of an Injection Fifo Group |
| * |
| * Set the "clear threshold crossed" status of the fifos that the specified fifo |
| * group has permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * \param[in] clr A 32-bit value, one bit per fifo. |
| * Only bits that the specified fifo group has |
| * permission to use should be set to 1. |
| * Set bit i to 1 to clear the threshold crossed status |
| * of fifo i. |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetClearThresholdCrossed( |
| DMA_InjFifoGroup_t *fg_ptr, |
| unsigned int clr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| SPI_assert( (clr & fg_ptr->permissions) == clr ); |
| |
| fg_ptr->status_ptr->clear_threshold_crossed = clr; |
| |
| } |
| |
| |
| /*! |
| * \brief Get the "activated" Status of an Injection Fifo Group |
| * |
| * Get the "activated" status of the fifos that the specified fifo |
| * group has permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * |
| * \retval activatedStatus A 32-bit value, one bit per fifo. |
| * Bit i is 1 if the specified fifo group has |
| * permission to use fifo i and fifo i is activated |
| * Bit i is 0 if the specified fifo group either |
| * does not have permission to use fifo i, or fifo i |
| * is not activated. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetActivated( |
| DMA_InjFifoGroup_t *fg_ptr |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| |
| return ( fg_ptr->status_ptr->activated & fg_ptr->permissions ); |
| |
| } |
| |
| |
| /*! |
| * \brief Set the "activate" Status of an Injection Fifo Group |
| * |
| * Set the "activate" status of the fifos that the specified fifo |
| * group has permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * \param[in] act A 32-bit value, one bit per fifo. |
| * Only bits that the specified fifo group has |
| * permission to use should be set to 1. |
| * Set bit i to 1 to activate fifo i. |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetActivate( |
| DMA_InjFifoGroup_t *fg_ptr, |
| unsigned int act |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| SPI_assert( (act & fg_ptr->permissions) == act ); |
| |
| fg_ptr->status_ptr->activate = act; |
| |
| } |
| |
| |
| /*! |
| * \brief Set the "deactivate" Status of an Injection Fifo Group |
| * |
| * Set the "deactivate" status of the fifos that the specified fifo |
| * group has permission to use. |
| * |
| * \param[in] fg_ptr Pointer to the injection fifo group structure |
| * \param[in] deact A 32-bit value, one bit per fifo. |
| * Only bits that the specified fifo group has |
| * permission to use should be set to 1. |
| * Set bit i to 1 to deactivate fifo i. |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetDeactivate( |
| DMA_InjFifoGroup_t *fg_ptr, |
| unsigned int deact |
| ) |
| { |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fg_ptr->status_ptr != NULL ); |
| SPI_assert( (deact & fg_ptr->permissions) == deact ); |
| |
| fg_ptr->status_ptr->deactivate = deact; |
| |
| } |
| |
| |
| |
| |
| /* |
| * ----------------------------------------------------------------------------- |
| * Calls to access the Fifo, given a fifo group and a fifo ID |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| |
| |
| |
| /*! |
| * \brief DMA InjFifo Initialization By Id |
| * |
| * - For an allocated injection DMA fifo, initialize its start, head, tail, and |
| * end. |
| * - Compute fifo size and free space. |
| * - Initialize descriptor count. |
| * - Activate the fifo. |
| * |
| * \param[in] fg_ptr Pointer to fifo group structure. |
| * \param[in] fifo_id Id of the fifo to be initialized |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] va_start Virtual address of the start of the fifo. |
| * \param[in] va_head Virtual address of the head of the fifo (typically |
| * equal to va_start). |
| * \param[in] va_end Virtual address of the end of the fifo. |
| * |
| * \retval 0 Successful. |
| * \retval -1 Unsuccessful. Error checks include |
| * - va_start < va_end |
| * - va_start <= va_head <= |
| * (va_end - DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS) |
| * - va_start and va_end are 32-byte aligned |
| * - fifo_size is larger than (DMA_MIN_INJECT_SIZE_IN_QUADS + |
| * DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS) |
| * |
| */ |
| __INLINE__ int DMA_InjFifoInitById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| void *va_start, |
| void *va_head, |
| void *va_end |
| ) |
| { |
| int rc; |
| |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( fifo_id >= 0 && fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP ); |
| SPI_assert( (fg_ptr->permissions & _BN(fifo_id)) != 0 ); |
| SPI_assert( va_start < va_end ); |
| SPI_assert( va_start <= va_head ); |
| SPI_assert( ((uint32_t) va_head) <= ( ((uint32_t) va_end) - DMA_FIFO_DESCRIPTOR_SIZE_IN_BYTES) ); |
| SPI_assert( ( ( (uint32_t) va_start) & 0xFFFFFFE0) == (uint32_t) va_start ); |
| SPI_assert( ( ( (uint32_t) va_end ) & 0xFFFFFFE0) == (uint32_t) va_end ); |
| SPI_assert( ( (unsigned)va_end - (unsigned)va_start ) >= |
| ( (DMA_MIN_INJECT_SIZE_IN_QUADS + DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS) * 16 ) ); |
| |
| /* |
| * Initialize the fifo by invoking a system call. This system call |
| * deactivates the fifo, initializes the start, end, head, and tail, |
| * and activates the fifo. |
| */ |
| |
| rc = Kernel_InjFifoInitById( |
| (uint32_t*)fg_ptr, |
| fifo_id, |
| (uint32_t*)va_start, |
| (uint32_t*)va_head, |
| (uint32_t*) va_end |
| ); |
| |
| if (rc) return rc; |
| |
| /* Initialize the descriptor count */ |
| fg_ptr->fifos[fifo_id].desc_count = 0; |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Start Pointer from the Shadow Using a Fifo Id |
| * |
| * Get a DMA injection fifo's start pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval va_start The virtual address of the start of the fifo |
| * |
| */ |
| __INLINE__ void * DMA_InjFifoGetStartFromShadowById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetStartFromShadow( &fg_ptr->fifos[fifo_id].dma_fifo ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Head Pointer Using a Fifo Id |
| * |
| * Get a DMA injection fifo's head pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval va_head The virtual address of the head of the fifo. |
| * |
| */ |
| __INLINE__ void * DMA_InjFifoGetHeadById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetHead( &fg_ptr->fifos[fifo_id].dma_fifo ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Tail Pointer Using a Fifo Id |
| * |
| * Get a DMA injection fifo's tail pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval va_tail The virtual address of the tail of the fifo |
| * |
| */ |
| __INLINE__ void *DMA_InjFifoGetTailById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetTail( &fg_ptr->fifos[fifo_id].dma_fifo ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo End Pointer from the Shadow Using a Fifo Id |
| * |
| * Get a DMA injection fifo's end pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval va_end The virtual address of the end of the fifo |
| * |
| */ |
| __INLINE__ void *DMA_InjFifoGetEndById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetEndFromShadow( &fg_ptr->fifos[fifo_id].dma_fifo ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Size Using a Fifo Id |
| * |
| * Get a DMA injection fifo's size, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval size The size of the DMA fifo, in units of 16B quads. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoGetSizeById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetSize( &fg_ptr->fifos[fifo_id].dma_fifo ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Free Space Using a Fifo Id |
| * |
| * Get a DMA injection fifo's free space, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] read_head Indicates whether to read the head from the hardware |
| * fifo before calculating the free space. |
| * - 1 means to read the hardware head |
| * - 0 means to use the current head shadow |
| * \param[in] read_tail Indicates whether to read the tail from the hardware |
| * fifo before calculating the free space. |
| * - 1 means to read the hardware tail |
| * - 0 means to use the current tail shadow |
| * |
| * \retval freeSpace The amount of free space in the fifo, in units of |
| * 16B quads. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoGetFreeSpaceById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| unsigned int read_head, |
| unsigned int read_tail |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_FifoGetFreeSpace( &fg_ptr->fifos[fifo_id].dma_fifo, |
| read_head, |
| read_tail ); |
| } |
| |
| |
| /*! |
| * \brief Set DMA InjFifo Head Pointer Using a Fifo Id |
| * |
| * Set a DMA injection fifo's head pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] va_head The virtual address of the head of the fifo. |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetHeadById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| void *va_head |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoSetHead( &fg_ptr->fifos[fifo_id], |
| va_head); |
| } |
| |
| |
| /*! |
| * \brief Set DMA InjFifo Tail Pointer Using a Fifo Id |
| * |
| * Set a DMA injection fifo's tail pointer, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] va_tail The virtual address of the tail of the fifo. |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetTailById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| void *va_tail |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_FifoSetTail( &fg_ptr->fifos[fifo_id].dma_fifo, |
| va_tail); |
| } |
| |
| |
| /*! |
| * \brief Increment DMA InjFifo Tail Pointer Using a Fifo Id |
| * |
| * Increment a DMA injection fifo's tail pointer, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] incr The number of quads (16 byte units) to increment the |
| * tail pointer by. |
| * |
| * \return None |
| * |
| * \note This function does not check if there is free space in the fifo |
| * for this many quads. It must be preceeded by a check of the |
| * free space. |
| */ |
| __INLINE__ void DMA_InjFifoIncrementTailById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| unsigned int incr |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoIncrementTail( &fg_ptr->fifos[fifo_id], |
| incr); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Descriptor Count Using a Fifo Id |
| * |
| * Get a DMA injection fifo's descriptor count, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ unsigned long long DMA_InjFifoGetDescriptorCountById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_InjFifoGetDescriptorCount( &fg_ptr->fifos[fifo_id] ); |
| } |
| |
| |
| /*! |
| * \brief Is DMA Descriptor Done Using a Fifo Id |
| * |
| * Return whether a specified descriptor is still in the specified injection |
| * fifo (not done). The descriptor is identified by the descriptor count |
| * immediately after the descriptor was injected into the fifo (returned by |
| * DMA_InjFifoIncrementTail(). |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] desc_count The descriptor count immediately after the |
| * descriptor in question was injected into |
| * the fifo. |
| * \param[in] update 0 Do not update the fifo's shadow information. |
| * 1 Update the fifo's shadow information. |
| * It is a performance optimization to only update the |
| * shadow information once for a group of descriptors |
| * being processed. |
| * |
| * \retval 0 False. The descriptor identified by desc_count is not done. |
| * It is still in the fifo. |
| * \retval 1 True. The descriptor identified by desc_count is done. |
| * It is no longer in the fifo. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoIsDescriptorDoneById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| unsigned long long desc_count, |
| unsigned int update |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return(DMA_InjFifoIsDescriptorDone( &fg_ptr->fifos[fifo_id], |
| desc_count, |
| update ) ); |
| |
| } |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Reserve Descriptor Storage Using a Fifo Id |
| * |
| * Reserve storage in a DMA injection fifo for a remote get descriptor, given |
| * a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval 0 Successful. There was enough space in the fifo and the |
| * storage was reserved. |
| * \retval -1 Unsuccessful. There was not enough space in the fifo. |
| * |
| * \note Internally, this increments the occupiedSize of the fifo by |
| * DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS. |
| * |
| */ |
| __INLINE__ int DMA_InjFifoReserveDescriptorStorageById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return ( DMA_InjFifoReserveDescriptorStorage( &fg_ptr->fifos[fifo_id] ) ); |
| } |
| |
| |
| /*! |
| * \brief DMA Injection Fifo Free Descriptor Storage Reservation Using a Fifo Id |
| * |
| * Free a reservation for storage for a remote get descriptor in a DMA injection |
| * fifo, previously reserved using DMA_InjFifoReserveDescriptorStorageById(). |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return None |
| * |
| * \note Internally, this decrements the occupiedSize of the fifo by |
| * DMA_FIFO_DESCRIPTOR_SIZE_IN_QUADS. |
| * |
| */ |
| __INLINE__ void DMA_InjFifoFreeDescriptorStorageReservationById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoFreeDescriptorStorageReservation( &fg_ptr->fifos[fifo_id] ); |
| return; |
| } |
| |
| |
| /*! |
| * \brief Check If An Injection Fifo Has Space For Injection Using a Fifo Id |
| * |
| * Check if an injection fifo has enough space for a single descriptor to be |
| * injected, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \retval hasSpace An indicator of whether the fifo has space for a |
| * descriptor. |
| * - 0 (false) means the fifo is full. |
| * - 1 (true) means the fifo has space. |
| * |
| */ |
| __INLINE__ unsigned int DMA_InjFifoHasSpaceById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_InjFifoHasSpace( &fg_ptr->fifos[fifo_id] ); |
| } |
| |
| |
| /*! |
| * \brief Inject a Descriptor into a DMA Injection Fifo Without Checking for |
| * Space Using a Fifo Id |
| * |
| * Inject a descriptor into a DMA injection fifo, given a fifo group and |
| * fifo id, without checking to see if there is enough space in the fifo. |
| * It is assumed that the caller has already checked for enough space using |
| * the DMA_InjFifoHasSpaceById() function. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] desc A pointer to the descriptor to be injected. |
| * Must be 16-byte aligned. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - 1 means it was successfully injected. |
| * |
| * \see DMA_InjFifoHasSpaceById() |
| */ |
| __INLINE__ int DMA_InjFifoInjectDescriptorNoSpaceCheckById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| DMA_InjDescriptor_t *desc |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_InjFifoInjectDescriptorNoSpaceCheck( &fg_ptr->fifos[fifo_id], |
| desc ); |
| } |
| |
| |
| /*! |
| * \brief Inject Descriptor into a DMA InjFifo Using a Fifo Id |
| * |
| * Inject a descriptor into a DMA injection fifo, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] desc Pointer to the descriptor to be injected into the fifo. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - 0 means it was not injected, most likely because |
| * the fifo is full. |
| * - 1 means it was successfully injected |
| * |
| */ |
| __INLINE__ int DMA_InjFifoInjectDescriptorById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| DMA_InjDescriptor_t *desc |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_InjFifoInjectDescriptor( &fg_ptr->fifos[fifo_id], |
| desc ); |
| } |
| |
| |
| /*! |
| * \brief Inject Multiple Descriptors into a DMA InjFifo Using a Fifo Id |
| * |
| * Inject multiple descriptors into a DMA injection fifo, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * \param[in] num_desc Number of descriptors to be injected |
| * \param[in] desc Pointer to an array of pointers to the descriptors to |
| * be injected into the fifo. |
| * |
| * \retval numDescInjected The number of descriptors injected. |
| * - less than num_desc means that some were not |
| * injected, most likely because the fifo is full. |
| * - equal to num_desc means that all were |
| * successfully injected. |
| * |
| */ |
| __INLINE__ int DMA_InjFifoInjectDescriptorsById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id, |
| int num_desc, |
| DMA_InjDescriptor_t **desc |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return DMA_InjFifoInjectDescriptors ( &fg_ptr->fifos[fifo_id], |
| num_desc, |
| desc ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Not Empty Status Using a Fifo Id |
| * |
| * Get a DMA injection fifo's not empty status, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return 32-bit status. status bit "fifo_id" is 1 if the |
| * fifo is not empty, 0 if empty. That is, the return value |
| * is 0 if empty, non-zero if not empty. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetNotEmptyById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return ( DMA_InjFifoGetNotEmpty( fg_ptr ) & _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Available Status Using a Fifo Id |
| * |
| * Get a DMA injection fifo's available status, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return 32-bit status. status bit fifo_id is 1 if the |
| * fifo is available, 0 if empty. |
| * |
| * \note "available" status means the fifo is enabled and activated. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetAvailableById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return ( DMA_InjFifoGetAvailable( fg_ptr ) & _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Threshold Crossed Status Using a Fifo Id |
| * |
| * Get a DMA injection fifo's threshold crossed status, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return 32-bit status. status bit fifo_id is 1 if the |
| * fifo's threshold has been crossed, 0 if not. |
| * |
| * \note This will always be zero. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetThresholdCrossedById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return ( DMA_InjFifoGetThresholdCrossed( fg_ptr ) & _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Clear DMA InjFifo Threshold Crossed Status Using a Fifo Id |
| * |
| * Clear a DMA injection fifo's threshold crossed status, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetClearThresholdCrossedById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoSetClearThresholdCrossed( fg_ptr, |
| _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Get DMA InjFifo Activated Status Using a Fifo Id |
| * |
| * Get a DMA injection fifo's activated status, given a fifo group and |
| * fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return 32-bit status. status bit fifo_id is 1 if the |
| * fifo is activated, 0 if empty. |
| * |
| */ |
| __INLINE__ unsigned DMA_InjFifoGetActivatedById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| return ( DMA_InjFifoGetActivated( fg_ptr ) & _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Activate DMA InjFifo Using a Fifo Id |
| * |
| * Activate a DMA injection fifo, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetActivateById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoSetActivate( fg_ptr, |
| _BN(fifo_id) ); |
| } |
| |
| |
| /*! |
| * \brief Deactivate DMA InjFifo Using a Fifo Id |
| * |
| * Deactivate a DMA injection fifo, given a fifo group and fifo id. |
| * |
| * \param[in] fg_ptr Pointer to the fifo group structure |
| * \param[in] fifo_id Id of the fifo within the group |
| * (0 to DMA_NUM_INJ_FIFOS_PER_GROUP-1). |
| * |
| * \return None |
| * |
| */ |
| __INLINE__ void DMA_InjFifoSetDeactivateById( |
| DMA_InjFifoGroup_t *fg_ptr, |
| int fifo_id |
| ) |
| { |
| SPI_assert( (fifo_id >= 0) && (fifo_id < DMA_NUM_INJ_FIFOS_PER_GROUP) ); |
| SPI_assert( fg_ptr != NULL ); |
| SPI_assert( ( fg_ptr->permissions & _BN(fifo_id) ) != 0 ); |
| |
| DMA_InjFifoSetDeactivate( fg_ptr, |
| _BN(fifo_id) ); |
| } |
| |
| |
| __END_DECLS |
| |
| |
| #endif |