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