]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/contrib/octeon-sdk/cvmx-tim.h
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / contrib / octeon-sdk / cvmx-tim.h
1 /***********************license start***************
2  *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3  *  reserved.
4  *
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions are
8  *  met:
9  *
10  *      * Redistributions of source code must retain the above copyright
11  *        notice, this list of conditions and the following disclaimer.
12  *
13  *      * Redistributions in binary form must reproduce the above
14  *        copyright notice, this list of conditions and the following
15  *        disclaimer in the documentation and/or other materials provided
16  *        with the distribution.
17  *
18  *      * Neither the name of Cavium Networks nor the names of
19  *        its contributors may be used to endorse or promote products
20  *        derived from this software without specific prior written
21  *        permission.
22  *
23  *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24  *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25  *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26  *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27  *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28  *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29  *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30  *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31  *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32  *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33  *
34  *
35  *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36  *
37  ***********************license end**************************************/
38
39
40
41
42
43
44 /**
45  * @file
46  *
47  * Interface to the hardware work queue timers.
48  *
49 `* <hr>$Revision: 42186 $<hr>
50  */
51
52 #ifndef __CVMX_TIM_H__
53 #define __CVMX_TIM_H__
54
55 #include "cvmx-fpa.h"
56 #include "cvmx-wqe.h"
57
58 #include "executive-config.h"
59 #ifdef CVMX_ENABLE_TIMER_FUNCTIONS
60 #include "cvmx-config.h"
61 #endif
62
63 #ifdef  __cplusplus
64 extern "C" {
65 #endif
66
67 #define CVMX_TIM_NUM_TIMERS   16
68 #define CVMX_TIM_NUM_BUCKETS  2048
69
70 typedef enum
71 {
72     CVMX_TIM_STATUS_SUCCESS = 0,
73     CVMX_TIM_STATUS_NO_MEMORY = -1,
74     CVMX_TIM_STATUS_TOO_FAR_AWAY = -2,
75     CVMX_TIM_STATUS_BUSY = -3
76 } cvmx_tim_status_t;
77
78 /**
79  * Each timer bucket contains a list of work queue entries to
80  * schedule when the timer fires. The list is implemented as
81  * a linked list of blocks. Each block contains an array of
82  * work queue entries followed by a next block pointer. Since
83  * these blocks are dynamically allocated off of a hardware
84  * memory pool, there actual size isn't known compile time.
85  * The next block pointer is stored in the last 8 bytes of
86  * the memory block.
87  */
88 typedef struct cvmx_tim_entry_chunk
89 {
90     volatile uint64_t entries[0];
91 } cvmx_tim_entry_chunk_t;
92
93 /**
94  * Each timer contains an array of buckets. Each bucket
95  * represents the list of work queue entries that should be
96  * scheduled when the timer fires.  The first 3 entries are used
97  * byt the hardware.
98  */
99 typedef struct
100 {
101    volatile uint64_t                first_chunk_addr;
102    volatile uint32_t                num_entries;    /**< Zeroed by HW after traversing list */
103    volatile uint32_t                chunk_remainder;/**< Zeroed by HW after traversing list */
104
105    // the remaining 16 bytes are not touched by hardware
106    volatile cvmx_tim_entry_chunk_t *last_chunk;
107    uint64_t                         pad;
108 } cvmx_tim_bucket_entry_t;
109
110 /**
111  * Structure representing an individual timer. Each timer has
112  * a timer period, a memory management pool, and a list of
113  * buckets.
114  */
115 typedef struct
116 {
117     cvmx_tim_bucket_entry_t*bucket;             /**< The timer buckets. Array of [CVMX_TIM_NUM_TIMERS][CVMX_TIM_NUM_BUCKETS] */
118     uint64_t                tick_cycles;        /**< How long a bucket represents */
119     uint64_t                start_time;         /**< Time the timer started in cycles */
120     uint32_t                bucket_shift;       /**< How long a bucket represents in ms */
121     uint32_t                num_buckets;        /**< How many buckets per wheel */
122     uint32_t                max_ticks;          /**< maximum number of ticks allowed for timer */
123 } cvmx_tim_t;
124
125 /**
126  * Structure used to store state information needed to delete
127  * an already scheduled timer entry. An instance of this
128  * structure must be passed to cvmx_tim_add_entry in order
129  * to be able to delete an entry later with
130  * cvmx_tim_delete_entry.
131  *
132  * NOTE: This structure should be considered opaque by the application,
133  * and the application should not access its members
134  */
135 typedef struct
136 {
137     uint64_t        commit_cycles;  /**< After this time the timer can't be changed */
138     uint64_t *      timer_entry_ptr;/**< Where the work entry is. Zero this
139                                             location to delete the entry */
140 } cvmx_tim_delete_t;
141
142 /**
143  * Global structure holding the state of all timers.
144  */
145 extern cvmx_tim_t cvmx_tim;
146
147
148
149
150 #ifdef CVMX_ENABLE_TIMER_FUNCTIONS
151 /**
152  * Setup a timer for use. Must be called before the timer
153  * can be used.
154  *
155  * @param tick      Time between each bucket in microseconds. This must not be
156  *                  smaller than 1024/(clock frequency in MHz).
157  * @param max_ticks The maximum number of ticks the timer must be able
158  *                  to schedule in the future. There are guaranteed to be enough
159  *                  timer buckets such that:
160  *                  number of buckets >= max_ticks.
161  * @return Zero on success. Negative on error. Failures are possible
162  *         if the number of buckets needed is too large or memory
163  *         allocation fails for creating the buckets.
164  */
165 int cvmx_tim_setup(uint64_t tick, uint64_t max_ticks);
166 #endif
167
168 /**
169  * Start the hardware timer processing
170  */
171 extern void cvmx_tim_start(void);
172
173
174 /**
175  * Stop the hardware timer processing. Timers stay configured.
176  */
177 extern void cvmx_tim_stop(void);
178
179
180 /**
181  * Stop the timer. After this the timer must be setup again
182  * before use.
183  */
184 #ifdef CVMX_ENABLE_TIMER_FUNCTIONS
185 extern void cvmx_tim_shutdown(void);
186 #endif
187
188 #ifdef CVMX_ENABLE_TIMER_FUNCTIONS
189 /**
190  * Add a work queue entry to the timer.
191  *
192  * @param work_entry Work queue entry to add.
193  * @param ticks_from_now
194  * @param delete_info
195  *                   Optional pointer where to store information needed to
196  *                   delete the timer entry. If non NULL information needed
197  *                   to delete the timer entry before it fires is stored here.
198  *                   If you don't need to be able to delete the timer, pass
199  *                   NULL.
200  * @return Result return code
201  */
202 static inline cvmx_tim_status_t cvmx_tim_add_entry(cvmx_wqe_t *work_entry, uint64_t ticks_from_now, cvmx_tim_delete_t *delete_info)
203 {
204     cvmx_tim_bucket_entry_t*    work_bucket_ptr;
205     uint64_t                    current_bucket;
206     uint64_t                    work_bucket;
207     volatile uint64_t         * tim_entry_ptr;  /* pointer to wqe address in timer chunk */
208     uint64_t                    entries_per_chunk;
209
210     const uint64_t  cycles  = cvmx_get_cycle(); /* Get our reference time early for accuracy */
211     const uint64_t  core_num    = cvmx_get_core_num();  /* One timer per processor, so use this to select */
212
213     /* Make sure the specified time won't wrap our bucket list */
214     if (ticks_from_now > cvmx_tim.max_ticks)
215     {
216         cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too far away.\n");
217         return CVMX_TIM_STATUS_TOO_FAR_AWAY;
218     }
219
220     /* Since we have no way to synchronize, we can't update a timer that is
221         being used by the hardware. Two buckets forward should be safe */
222     if (ticks_from_now < 2)
223     {
224         cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too soon. Delaying it.\n");
225         ticks_from_now = 2;
226     }
227
228     /* Get the bucket this work queue entry should be in. Remember the bucket
229         array is circular */
230     current_bucket = ((cycles - cvmx_tim.start_time)
231                    >> cvmx_tim.bucket_shift);
232     work_bucket = (((ticks_from_now * cvmx_tim.tick_cycles) + cycles - cvmx_tim.start_time)
233                    >> cvmx_tim.bucket_shift);
234
235     work_bucket_ptr = cvmx_tim.bucket + core_num * cvmx_tim.num_buckets + (work_bucket & (cvmx_tim.num_buckets - 1));
236     entries_per_chunk = (CVMX_FPA_TIMER_POOL_SIZE/8 - 1);
237
238     /* Check if we have room to add this entry into the existing list */
239     if (work_bucket_ptr->chunk_remainder)
240     {
241         /* Adding the work entry to the end of the existing list */
242         tim_entry_ptr = &(work_bucket_ptr->last_chunk->entries[entries_per_chunk - work_bucket_ptr->chunk_remainder]);
243         *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
244         work_bucket_ptr->chunk_remainder--;
245         work_bucket_ptr->num_entries++;
246     }
247     else
248     {
249         /* Current list is either completely empty or completely full. We need
250             to allocate a new chunk for storing this work entry */
251         cvmx_tim_entry_chunk_t *new_chunk = (cvmx_tim_entry_chunk_t *)cvmx_fpa_alloc(CVMX_FPA_TIMER_POOL);
252         if (new_chunk == NULL)
253         {
254             cvmx_dprintf("cvmx_tim_add_entry: Failed to allocate memory for new chunk.\n");
255             return CVMX_TIM_STATUS_NO_MEMORY;
256         }
257
258         /* Does a chunk currently exist? We have to check num_entries since
259             the hardware doesn't NULL out the chunk pointers on free */
260         if (work_bucket_ptr->num_entries)
261         {
262             /* This chunk must be appended to an existing list by putting
263             ** its address in the last spot of the existing chunk. */
264             work_bucket_ptr->last_chunk->entries[entries_per_chunk] = cvmx_ptr_to_phys(new_chunk);
265             work_bucket_ptr->num_entries++;
266         }
267         else
268         {
269             /* This is the very first chunk. Add it */
270             work_bucket_ptr->first_chunk_addr = cvmx_ptr_to_phys(new_chunk);
271             work_bucket_ptr->num_entries = 1;
272         }
273         work_bucket_ptr->last_chunk = new_chunk;
274         work_bucket_ptr->chunk_remainder = entries_per_chunk - 1;
275         tim_entry_ptr = &(new_chunk->entries[0]);
276         *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
277     }
278
279     /* If the user supplied a delete info structure then fill it in */
280     if (delete_info)
281     {
282         /* It would be very bad to delete a timer entry after, or during the
283             timer's processing. During the processing could yield unpredicatable
284             results, but after would always be bad. Modifying the entry after
285             processing means we would be changing data in a buffer that has been
286             freed, and possible allocated again. For this reason we store a
287             commit cycle count in the delete structure. If we are after this
288             count we will refuse to delete the timer entry. */
289         delete_info->commit_cycles = cycles + (ticks_from_now - 2) * cvmx_tim.tick_cycles;
290         delete_info->timer_entry_ptr = (uint64_t *)tim_entry_ptr;  /* Cast to non-volatile type */
291     }
292
293     CVMX_SYNCWS; /* Make sure the hardware timer unit can access valid data from L2 */
294
295     return CVMX_TIM_STATUS_SUCCESS;
296 }
297 #endif
298
299
300 /**
301  * Delete a timer entry scheduled using cvmx_tim_add_entry.
302  * Deleting a timer will fail if it has already triggered or
303  * might be in progress. The actual state of the work queue
304  * entry isn't changed. You need to dispose of it properly.
305  *
306  * @param delete_info
307  *               Structure passed to cvmx_tim_add_entry to store the
308  *               information needed to delete a timer entry.
309  * @return CVMX_TIM_STATUS_BUSY if the timer was not deleted, otherwise
310  *         CVMX_TIM_STATUS_SUCCESS.
311  */
312 static inline cvmx_tim_status_t cvmx_tim_delete_entry(cvmx_tim_delete_t *delete_info)
313 {
314     const uint64_t cycles = cvmx_get_cycle();
315
316     if ((int64_t)(cycles - delete_info->commit_cycles) < 0)
317     {
318         /* Timer is far enough away. Safe to delete */
319         *delete_info->timer_entry_ptr = 0;
320         return CVMX_TIM_STATUS_SUCCESS;
321     }
322     else
323     {
324         /* Timer is passed the commit time. It cannot be stopped */
325         return CVMX_TIM_STATUS_BUSY;
326     }
327 }
328
329 #ifdef  __cplusplus
330 }
331 #endif
332
333 #endif // __CVMX_TIM_H__