cvmx-tim.h revision 210284
1210284Sjmallett/***********************license start***************
2210284Sjmallett *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3210284Sjmallett *  reserved.
4210284Sjmallett *
5210284Sjmallett *
6210284Sjmallett *  Redistribution and use in source and binary forms, with or without
7210284Sjmallett *  modification, are permitted provided that the following conditions are
8210284Sjmallett *  met:
9210284Sjmallett *
10210284Sjmallett *      * Redistributions of source code must retain the above copyright
11210284Sjmallett *        notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13210284Sjmallett *      * Redistributions in binary form must reproduce the above
14210284Sjmallett *        copyright notice, this list of conditions and the following
15210284Sjmallett *        disclaimer in the documentation and/or other materials provided
16210284Sjmallett *        with the distribution.
17210284Sjmallett *
18210284Sjmallett *      * Neither the name of Cavium Networks nor the names of
19210284Sjmallett *        its contributors may be used to endorse or promote products
20210284Sjmallett *        derived from this software without specific prior written
21210284Sjmallett *        permission.
22210284Sjmallett *
23210284Sjmallett *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24210284Sjmallett *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25210284Sjmallett *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26210284Sjmallett *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27210284Sjmallett *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28210284Sjmallett *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29210284Sjmallett *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30210284Sjmallett *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31210284Sjmallett *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32210284Sjmallett *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33210284Sjmallett *
34210284Sjmallett *
35210284Sjmallett *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36210284Sjmallett *
37210284Sjmallett ***********************license end**************************************/
38210284Sjmallett
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett
44210284Sjmallett/**
45210284Sjmallett * @file
46210284Sjmallett *
47210284Sjmallett * Interface to the hardware work queue timers.
48210284Sjmallett *
49210284Sjmallett`* <hr>$Revision: 42186 $<hr>
50210284Sjmallett */
51210284Sjmallett
52210284Sjmallett#ifndef __CVMX_TIM_H__
53210284Sjmallett#define __CVMX_TIM_H__
54210284Sjmallett
55210284Sjmallett#include "cvmx-fpa.h"
56210284Sjmallett#include "cvmx-wqe.h"
57210284Sjmallett
58210284Sjmallett#include "executive-config.h"
59210284Sjmallett#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
60210284Sjmallett#include "cvmx-config.h"
61210284Sjmallett#endif
62210284Sjmallett
63210284Sjmallett#ifdef	__cplusplus
64210284Sjmallettextern "C" {
65210284Sjmallett#endif
66210284Sjmallett
67210284Sjmallett#define CVMX_TIM_NUM_TIMERS   16
68210284Sjmallett#define CVMX_TIM_NUM_BUCKETS  2048
69210284Sjmallett
70210284Sjmalletttypedef enum
71210284Sjmallett{
72210284Sjmallett    CVMX_TIM_STATUS_SUCCESS = 0,
73210284Sjmallett    CVMX_TIM_STATUS_NO_MEMORY = -1,
74210284Sjmallett    CVMX_TIM_STATUS_TOO_FAR_AWAY = -2,
75210284Sjmallett    CVMX_TIM_STATUS_BUSY = -3
76210284Sjmallett} cvmx_tim_status_t;
77210284Sjmallett
78210284Sjmallett/**
79210284Sjmallett * Each timer bucket contains a list of work queue entries to
80210284Sjmallett * schedule when the timer fires. The list is implemented as
81210284Sjmallett * a linked list of blocks. Each block contains an array of
82210284Sjmallett * work queue entries followed by a next block pointer. Since
83210284Sjmallett * these blocks are dynamically allocated off of a hardware
84210284Sjmallett * memory pool, there actual size isn't known compile time.
85210284Sjmallett * The next block pointer is stored in the last 8 bytes of
86210284Sjmallett * the memory block.
87210284Sjmallett */
88210284Sjmalletttypedef struct cvmx_tim_entry_chunk
89210284Sjmallett{
90210284Sjmallett    volatile uint64_t entries[0];
91210284Sjmallett} cvmx_tim_entry_chunk_t;
92210284Sjmallett
93210284Sjmallett/**
94210284Sjmallett * Each timer contains an array of buckets. Each bucket
95210284Sjmallett * represents the list of work queue entries that should be
96210284Sjmallett * scheduled when the timer fires.  The first 3 entries are used
97210284Sjmallett * byt the hardware.
98210284Sjmallett */
99210284Sjmalletttypedef struct
100210284Sjmallett{
101210284Sjmallett   volatile uint64_t                first_chunk_addr;
102210284Sjmallett   volatile uint32_t                num_entries;    /**< Zeroed by HW after traversing list */
103210284Sjmallett   volatile uint32_t                chunk_remainder;/**< Zeroed by HW after traversing list */
104210284Sjmallett
105210284Sjmallett   // the remaining 16 bytes are not touched by hardware
106210284Sjmallett   volatile cvmx_tim_entry_chunk_t *last_chunk;
107210284Sjmallett   uint64_t                         pad;
108210284Sjmallett} cvmx_tim_bucket_entry_t;
109210284Sjmallett
110210284Sjmallett/**
111210284Sjmallett * Structure representing an individual timer. Each timer has
112210284Sjmallett * a timer period, a memory management pool, and a list of
113210284Sjmallett * buckets.
114210284Sjmallett */
115210284Sjmalletttypedef struct
116210284Sjmallett{
117210284Sjmallett    cvmx_tim_bucket_entry_t*bucket;             /**< The timer buckets. Array of [CVMX_TIM_NUM_TIMERS][CVMX_TIM_NUM_BUCKETS] */
118210284Sjmallett    uint64_t                tick_cycles;        /**< How long a bucket represents */
119210284Sjmallett    uint64_t                start_time;         /**< Time the timer started in cycles */
120210284Sjmallett    uint32_t                bucket_shift;       /**< How long a bucket represents in ms */
121210284Sjmallett    uint32_t                num_buckets;        /**< How many buckets per wheel */
122210284Sjmallett    uint32_t                max_ticks;          /**< maximum number of ticks allowed for timer */
123210284Sjmallett} cvmx_tim_t;
124210284Sjmallett
125210284Sjmallett/**
126210284Sjmallett * Structure used to store state information needed to delete
127210284Sjmallett * an already scheduled timer entry. An instance of this
128210284Sjmallett * structure must be passed to cvmx_tim_add_entry in order
129210284Sjmallett * to be able to delete an entry later with
130210284Sjmallett * cvmx_tim_delete_entry.
131210284Sjmallett *
132210284Sjmallett * NOTE: This structure should be considered opaque by the application,
133210284Sjmallett * and the application should not access its members
134210284Sjmallett */
135210284Sjmalletttypedef struct
136210284Sjmallett{
137210284Sjmallett    uint64_t        commit_cycles;  /**< After this time the timer can't be changed */
138210284Sjmallett    uint64_t *      timer_entry_ptr;/**< Where the work entry is. Zero this
139210284Sjmallett                                            location to delete the entry */
140210284Sjmallett} cvmx_tim_delete_t;
141210284Sjmallett
142210284Sjmallett/**
143210284Sjmallett * Global structure holding the state of all timers.
144210284Sjmallett */
145210284Sjmallettextern cvmx_tim_t cvmx_tim;
146210284Sjmallett
147210284Sjmallett
148210284Sjmallett
149210284Sjmallett
150210284Sjmallett#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
151210284Sjmallett/**
152210284Sjmallett * Setup a timer for use. Must be called before the timer
153210284Sjmallett * can be used.
154210284Sjmallett *
155210284Sjmallett * @param tick      Time between each bucket in microseconds. This must not be
156210284Sjmallett *                  smaller than 1024/(clock frequency in MHz).
157210284Sjmallett * @param max_ticks The maximum number of ticks the timer must be able
158210284Sjmallett *                  to schedule in the future. There are guaranteed to be enough
159210284Sjmallett *                  timer buckets such that:
160210284Sjmallett *                  number of buckets >= max_ticks.
161210284Sjmallett * @return Zero on success. Negative on error. Failures are possible
162210284Sjmallett *         if the number of buckets needed is too large or memory
163210284Sjmallett *         allocation fails for creating the buckets.
164210284Sjmallett */
165210284Sjmallettint cvmx_tim_setup(uint64_t tick, uint64_t max_ticks);
166210284Sjmallett#endif
167210284Sjmallett
168210284Sjmallett/**
169210284Sjmallett * Start the hardware timer processing
170210284Sjmallett */
171210284Sjmallettextern void cvmx_tim_start(void);
172210284Sjmallett
173210284Sjmallett
174210284Sjmallett/**
175210284Sjmallett * Stop the hardware timer processing. Timers stay configured.
176210284Sjmallett */
177210284Sjmallettextern void cvmx_tim_stop(void);
178210284Sjmallett
179210284Sjmallett
180210284Sjmallett/**
181210284Sjmallett * Stop the timer. After this the timer must be setup again
182210284Sjmallett * before use.
183210284Sjmallett */
184210284Sjmallett#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
185210284Sjmallettextern void cvmx_tim_shutdown(void);
186210284Sjmallett#endif
187210284Sjmallett
188210284Sjmallett#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
189210284Sjmallett/**
190210284Sjmallett * Add a work queue entry to the timer.
191210284Sjmallett *
192210284Sjmallett * @param work_entry Work queue entry to add.
193210284Sjmallett * @param ticks_from_now
194210284Sjmallett * @param delete_info
195210284Sjmallett *                   Optional pointer where to store information needed to
196210284Sjmallett *                   delete the timer entry. If non NULL information needed
197210284Sjmallett *                   to delete the timer entry before it fires is stored here.
198210284Sjmallett *                   If you don't need to be able to delete the timer, pass
199210284Sjmallett *                   NULL.
200210284Sjmallett * @return Result return code
201210284Sjmallett */
202210284Sjmallettstatic 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)
203210284Sjmallett{
204210284Sjmallett    cvmx_tim_bucket_entry_t*    work_bucket_ptr;
205210284Sjmallett    uint64_t                    current_bucket;
206210284Sjmallett    uint64_t                    work_bucket;
207210284Sjmallett    volatile uint64_t         * tim_entry_ptr;  /* pointer to wqe address in timer chunk */
208210284Sjmallett    uint64_t                    entries_per_chunk;
209210284Sjmallett
210210284Sjmallett    const uint64_t  cycles  = cvmx_get_cycle(); /* Get our reference time early for accuracy */
211210284Sjmallett    const uint64_t  core_num    = cvmx_get_core_num();  /* One timer per processor, so use this to select */
212210284Sjmallett
213210284Sjmallett    /* Make sure the specified time won't wrap our bucket list */
214210284Sjmallett    if (ticks_from_now > cvmx_tim.max_ticks)
215210284Sjmallett    {
216210284Sjmallett        cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too far away.\n");
217210284Sjmallett        return CVMX_TIM_STATUS_TOO_FAR_AWAY;
218210284Sjmallett    }
219210284Sjmallett
220210284Sjmallett    /* Since we have no way to synchronize, we can't update a timer that is
221210284Sjmallett        being used by the hardware. Two buckets forward should be safe */
222210284Sjmallett    if (ticks_from_now < 2)
223210284Sjmallett    {
224210284Sjmallett        cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too soon. Delaying it.\n");
225210284Sjmallett        ticks_from_now = 2;
226210284Sjmallett    }
227210284Sjmallett
228210284Sjmallett    /* Get the bucket this work queue entry should be in. Remember the bucket
229210284Sjmallett        array is circular */
230210284Sjmallett    current_bucket = ((cycles - cvmx_tim.start_time)
231210284Sjmallett		   >> cvmx_tim.bucket_shift);
232210284Sjmallett    work_bucket = (((ticks_from_now * cvmx_tim.tick_cycles) + cycles - cvmx_tim.start_time)
233210284Sjmallett		   >> cvmx_tim.bucket_shift);
234210284Sjmallett
235210284Sjmallett    work_bucket_ptr = cvmx_tim.bucket + core_num * cvmx_tim.num_buckets + (work_bucket & (cvmx_tim.num_buckets - 1));
236210284Sjmallett    entries_per_chunk = (CVMX_FPA_TIMER_POOL_SIZE/8 - 1);
237210284Sjmallett
238210284Sjmallett    /* Check if we have room to add this entry into the existing list */
239210284Sjmallett    if (work_bucket_ptr->chunk_remainder)
240210284Sjmallett    {
241210284Sjmallett        /* Adding the work entry to the end of the existing list */
242210284Sjmallett        tim_entry_ptr = &(work_bucket_ptr->last_chunk->entries[entries_per_chunk - work_bucket_ptr->chunk_remainder]);
243210284Sjmallett        *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
244210284Sjmallett        work_bucket_ptr->chunk_remainder--;
245210284Sjmallett        work_bucket_ptr->num_entries++;
246210284Sjmallett    }
247210284Sjmallett    else
248210284Sjmallett    {
249210284Sjmallett        /* Current list is either completely empty or completely full. We need
250210284Sjmallett            to allocate a new chunk for storing this work entry */
251210284Sjmallett        cvmx_tim_entry_chunk_t *new_chunk = (cvmx_tim_entry_chunk_t *)cvmx_fpa_alloc(CVMX_FPA_TIMER_POOL);
252210284Sjmallett        if (new_chunk == NULL)
253210284Sjmallett        {
254210284Sjmallett            cvmx_dprintf("cvmx_tim_add_entry: Failed to allocate memory for new chunk.\n");
255210284Sjmallett            return CVMX_TIM_STATUS_NO_MEMORY;
256210284Sjmallett        }
257210284Sjmallett
258210284Sjmallett        /* Does a chunk currently exist? We have to check num_entries since
259210284Sjmallett            the hardware doesn't NULL out the chunk pointers on free */
260210284Sjmallett        if (work_bucket_ptr->num_entries)
261210284Sjmallett        {
262210284Sjmallett            /* This chunk must be appended to an existing list by putting
263210284Sjmallett            ** its address in the last spot of the existing chunk. */
264210284Sjmallett            work_bucket_ptr->last_chunk->entries[entries_per_chunk] = cvmx_ptr_to_phys(new_chunk);
265210284Sjmallett            work_bucket_ptr->num_entries++;
266210284Sjmallett        }
267210284Sjmallett        else
268210284Sjmallett        {
269210284Sjmallett            /* This is the very first chunk. Add it */
270210284Sjmallett            work_bucket_ptr->first_chunk_addr = cvmx_ptr_to_phys(new_chunk);
271210284Sjmallett            work_bucket_ptr->num_entries = 1;
272210284Sjmallett        }
273210284Sjmallett        work_bucket_ptr->last_chunk = new_chunk;
274210284Sjmallett        work_bucket_ptr->chunk_remainder = entries_per_chunk - 1;
275210284Sjmallett        tim_entry_ptr = &(new_chunk->entries[0]);
276210284Sjmallett        *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
277210284Sjmallett    }
278210284Sjmallett
279210284Sjmallett    /* If the user supplied a delete info structure then fill it in */
280210284Sjmallett    if (delete_info)
281210284Sjmallett    {
282210284Sjmallett        /* It would be very bad to delete a timer entry after, or during the
283210284Sjmallett            timer's processing. During the processing could yield unpredicatable
284210284Sjmallett            results, but after would always be bad. Modifying the entry after
285210284Sjmallett            processing means we would be changing data in a buffer that has been
286210284Sjmallett            freed, and possible allocated again. For this reason we store a
287210284Sjmallett            commit cycle count in the delete structure. If we are after this
288210284Sjmallett            count we will refuse to delete the timer entry. */
289210284Sjmallett        delete_info->commit_cycles = cycles + (ticks_from_now - 2) * cvmx_tim.tick_cycles;
290210284Sjmallett        delete_info->timer_entry_ptr = (uint64_t *)tim_entry_ptr;  /* Cast to non-volatile type */
291210284Sjmallett    }
292210284Sjmallett
293210284Sjmallett    CVMX_SYNCWS; /* Make sure the hardware timer unit can access valid data from L2 */
294210284Sjmallett
295210284Sjmallett    return CVMX_TIM_STATUS_SUCCESS;
296210284Sjmallett}
297210284Sjmallett#endif
298210284Sjmallett
299210284Sjmallett
300210284Sjmallett/**
301210284Sjmallett * Delete a timer entry scheduled using cvmx_tim_add_entry.
302210284Sjmallett * Deleting a timer will fail if it has already triggered or
303210284Sjmallett * might be in progress. The actual state of the work queue
304210284Sjmallett * entry isn't changed. You need to dispose of it properly.
305210284Sjmallett *
306210284Sjmallett * @param delete_info
307210284Sjmallett *               Structure passed to cvmx_tim_add_entry to store the
308210284Sjmallett *               information needed to delete a timer entry.
309210284Sjmallett * @return CVMX_TIM_STATUS_BUSY if the timer was not deleted, otherwise
310210284Sjmallett *         CVMX_TIM_STATUS_SUCCESS.
311210284Sjmallett */
312210284Sjmallettstatic inline cvmx_tim_status_t cvmx_tim_delete_entry(cvmx_tim_delete_t *delete_info)
313210284Sjmallett{
314210284Sjmallett    const uint64_t cycles = cvmx_get_cycle();
315210284Sjmallett
316210284Sjmallett    if ((int64_t)(cycles - delete_info->commit_cycles) < 0)
317210284Sjmallett    {
318210284Sjmallett        /* Timer is far enough away. Safe to delete */
319210284Sjmallett        *delete_info->timer_entry_ptr = 0;
320210284Sjmallett        return CVMX_TIM_STATUS_SUCCESS;
321210284Sjmallett    }
322210284Sjmallett    else
323210284Sjmallett    {
324210284Sjmallett        /* Timer is passed the commit time. It cannot be stopped */
325210284Sjmallett        return CVMX_TIM_STATUS_BUSY;
326210284Sjmallett    }
327210284Sjmallett}
328210284Sjmallett
329210284Sjmallett#ifdef	__cplusplus
330210284Sjmallett}
331210284Sjmallett#endif
332210284Sjmallett
333210284Sjmallett#endif // __CVMX_TIM_H__
334