cvmx-tim.h revision 215990
1190203Srpaulo/***********************license start***************
2190203Srpaulo * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3190203Srpaulo * reserved.
4190203Srpaulo *
5190203Srpaulo *
6190203Srpaulo * Redistribution and use in source and binary forms, with or without
7190203Srpaulo * modification, are permitted provided that the following conditions are
8190203Srpaulo * met:
9190203Srpaulo *
10190203Srpaulo *   * Redistributions of source code must retain the above copyright
11190203Srpaulo *     notice, this list of conditions and the following disclaimer.
12190203Srpaulo *
13190203Srpaulo *   * Redistributions in binary form must reproduce the above
14190203Srpaulo *     copyright notice, this list of conditions and the following
15190203Srpaulo *     disclaimer in the documentation and/or other materials provided
16190203Srpaulo *     with the distribution.
17190203Srpaulo
18190203Srpaulo *   * Neither the name of Cavium Networks nor the names of
19190203Srpaulo *     its contributors may be used to endorse or promote products
20190203Srpaulo *     derived from this software without specific prior written
21190203Srpaulo *     permission.
22190203Srpaulo
23190203Srpaulo * This Software, including technical data, may be subject to U.S. export  control
24190203Srpaulo * laws, including the U.S. Export Administration Act and its  associated
25190203Srpaulo * regulations, and may be subject to export or import  regulations in other
26190203Srpaulo * countries.
27190203Srpaulo
28190203Srpaulo * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29190203Srpaulo * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30190203Srpaulo * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31190203Srpaulo * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32190203Srpaulo * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33190203Srpaulo * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34190203Srpaulo * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35190203Srpaulo * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36190203Srpaulo * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37190203Srpaulo * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38190203Srpaulo ***********************license end**************************************/
39190203Srpaulo
40190203Srpaulo
41190203Srpaulo
42190203Srpaulo
43190203Srpaulo
44190203Srpaulo
45190203Srpaulo
46190203Srpaulo/**
47190203Srpaulo * @file
48190203Srpaulo *
49190203Srpaulo * Interface to the hardware work queue timers.
50190203Srpaulo *
51190203Srpaulo`* <hr>$Revision: 49448 $<hr>
52190203Srpaulo */
53190203Srpaulo
54190203Srpaulo#ifndef __CVMX_TIM_H__
55190203Srpaulo#define __CVMX_TIM_H__
56190203Srpaulo
57190203Srpaulo#include "cvmx-clock.h"
58190203Srpaulo#include "cvmx-fpa.h"
59190203Srpaulo#include "cvmx-wqe.h"
60190203Srpaulo
61190203Srpaulo#include "executive-config.h"
62190203Srpaulo#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
63190203Srpaulo#include "cvmx-config.h"
64190203Srpaulo#endif
65190203Srpaulo
66190203Srpaulo#ifdef	__cplusplus
67190203Srpauloextern "C" {
68190203Srpaulo#endif
69190203Srpaulo
70190203Srpaulo#define CVMX_TIM_NUM_TIMERS   16
71190203Srpaulo#define CVMX_TIM_NUM_BUCKETS  2048
72190203Srpaulo
73190203Srpaulotypedef enum
74190203Srpaulo{
75190203Srpaulo    CVMX_TIM_STATUS_SUCCESS = 0,
76190203Srpaulo    CVMX_TIM_STATUS_NO_MEMORY = -1,
77190203Srpaulo    CVMX_TIM_STATUS_TOO_FAR_AWAY = -2,
78190203Srpaulo    CVMX_TIM_STATUS_BUSY = -3
79190203Srpaulo} cvmx_tim_status_t;
80190203Srpaulo
81190203Srpaulo/**
82190203Srpaulo * Each timer bucket contains a list of work queue entries to
83190203Srpaulo * schedule when the timer fires. The list is implemented as
84190203Srpaulo * a linked list of blocks. Each block contains an array of
85190203Srpaulo * work queue entries followed by a next block pointer. Since
86190203Srpaulo * these blocks are dynamically allocated off of a hardware
87190203Srpaulo * memory pool, there actual size isn't known compile time.
88190203Srpaulo * The next block pointer is stored in the last 8 bytes of
89190203Srpaulo * the memory block.
90190203Srpaulo */
91190203Srpaulotypedef struct cvmx_tim_entry_chunk
92190203Srpaulo{
93190203Srpaulo    volatile uint64_t entries[0];
94190203Srpaulo} cvmx_tim_entry_chunk_t;
95190203Srpaulo
96190203Srpaulo/**
97190203Srpaulo * Each timer contains an array of buckets. Each bucket
98190203Srpaulo * represents the list of work queue entries that should be
99190203Srpaulo * scheduled when the timer fires.  The first 3 entries are used
100190203Srpaulo * byt the hardware.
101190203Srpaulo */
102190203Srpaulotypedef struct
103190203Srpaulo{
104190203Srpaulo   volatile uint64_t                first_chunk_addr;
105190203Srpaulo   volatile uint32_t                num_entries;    /**< Zeroed by HW after traversing list */
106190203Srpaulo   volatile uint32_t                chunk_remainder;/**< Zeroed by HW after traversing list */
107190203Srpaulo
108190203Srpaulo   // the remaining 16 bytes are not touched by hardware
109190203Srpaulo   volatile cvmx_tim_entry_chunk_t *last_chunk;
110190203Srpaulo   uint64_t                         pad;
111190203Srpaulo} cvmx_tim_bucket_entry_t;
112190203Srpaulo
113190203Srpaulo/**
114190203Srpaulo * Structure representing an individual timer. Each timer has
115190203Srpaulo * a timer period, a memory management pool, and a list of
116190203Srpaulo * buckets.
117190203Srpaulo */
118190203Srpaulotypedef struct
119190203Srpaulo{
120190203Srpaulo    cvmx_tim_bucket_entry_t*bucket;             /**< The timer buckets. Array of [CVMX_TIM_NUM_TIMERS][CVMX_TIM_NUM_BUCKETS] */
121190203Srpaulo    uint64_t                tick_cycles;        /**< How long a bucket represents */
122190203Srpaulo    uint64_t                start_time;         /**< Time the timer started in cycles */
123190203Srpaulo    uint32_t                bucket_shift;       /**< How long a bucket represents in ms */
124190203Srpaulo    uint32_t                num_buckets;        /**< How many buckets per wheel */
125190203Srpaulo    uint32_t                max_ticks;          /**< maximum number of ticks allowed for timer */
126190203Srpaulo} cvmx_tim_t;
127190203Srpaulo
128190203Srpaulo/**
129190203Srpaulo * Structure used to store state information needed to delete
130190203Srpaulo * an already scheduled timer entry. An instance of this
131190203Srpaulo * structure must be passed to cvmx_tim_add_entry in order
132190203Srpaulo * to be able to delete an entry later with
133190203Srpaulo * cvmx_tim_delete_entry.
134190203Srpaulo *
135190203Srpaulo * NOTE: This structure should be considered opaque by the application,
136190203Srpaulo * and the application should not access its members
137190203Srpaulo */
138190203Srpaulotypedef struct
139190203Srpaulo{
140190203Srpaulo    uint64_t        commit_cycles;  /**< After this time the timer can't be changed */
141190203Srpaulo    uint64_t *      timer_entry_ptr;/**< Where the work entry is. Zero this
142190203Srpaulo                                            location to delete the entry */
143190203Srpaulo} cvmx_tim_delete_t;
144190203Srpaulo
145190203Srpaulo/**
146190203Srpaulo * Global structure holding the state of all timers.
147190203Srpaulo */
148190203Srpauloextern cvmx_tim_t cvmx_tim;
149190203Srpaulo
150190203Srpaulo
151190203Srpaulo
152190203Srpaulo
153190203Srpaulo#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
154190203Srpaulo/**
155190203Srpaulo * Setup a timer for use. Must be called before the timer
156190203Srpaulo * can be used.
157190203Srpaulo *
158190203Srpaulo * @param tick      Time between each bucket in microseconds. This must not be
159190203Srpaulo *                  smaller than 1024/(clock frequency in MHz).
160190203Srpaulo * @param max_ticks The maximum number of ticks the timer must be able
161190203Srpaulo *                  to schedule in the future. There are guaranteed to be enough
162190203Srpaulo *                  timer buckets such that:
163190203Srpaulo *                  number of buckets >= max_ticks.
164190203Srpaulo * @return Zero on success. Negative on error. Failures are possible
165190203Srpaulo *         if the number of buckets needed is too large or memory
166190203Srpaulo *         allocation fails for creating the buckets.
167190203Srpaulo */
168190203Srpauloint cvmx_tim_setup(uint64_t tick, uint64_t max_ticks);
169190203Srpaulo#endif
170190203Srpaulo
171190203Srpaulo/**
172190203Srpaulo * Start the hardware timer processing
173190203Srpaulo */
174190203Srpauloextern void cvmx_tim_start(void);
175190203Srpaulo
176190203Srpaulo
177190203Srpaulo/**
178190203Srpaulo * Stop the hardware timer processing. Timers stay configured.
179190203Srpaulo */
180190203Srpauloextern void cvmx_tim_stop(void);
181190203Srpaulo
182190203Srpaulo
183190203Srpaulo/**
184190203Srpaulo * Stop the timer. After this the timer must be setup again
185190203Srpaulo * before use.
186190203Srpaulo */
187190203Srpaulo#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
188190203Srpauloextern void cvmx_tim_shutdown(void);
189190203Srpaulo#endif
190190203Srpaulo
191190203Srpaulo#ifdef CVMX_ENABLE_TIMER_FUNCTIONS
192190203Srpaulo/**
193190203Srpaulo * Add a work queue entry to the timer.
194190203Srpaulo *
195190203Srpaulo * @param work_entry Work queue entry to add.
196190203Srpaulo * @param ticks_from_now
197190203Srpaulo * @param delete_info
198190203Srpaulo *                   Optional pointer where to store information needed to
199190203Srpaulo *                   delete the timer entry. If non NULL information needed
200190203Srpaulo *                   to delete the timer entry before it fires is stored here.
201190203Srpaulo *                   If you don't need to be able to delete the timer, pass
202190203Srpaulo *                   NULL.
203190203Srpaulo * @return Result return code
204190203Srpaulo */
205190203Srpaulostatic 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)
206190203Srpaulo{
207190203Srpaulo    cvmx_tim_bucket_entry_t*    work_bucket_ptr;
208190203Srpaulo    uint64_t                    current_bucket;
209190203Srpaulo    uint64_t                    work_bucket;
210190203Srpaulo    volatile uint64_t         * tim_entry_ptr;  /* pointer to wqe address in timer chunk */
211190203Srpaulo    uint64_t                    entries_per_chunk;
212190203Srpaulo
213190203Srpaulo    const uint64_t  cycles  = cvmx_clock_get_count(CVMX_CLOCK_TIM); /* Get our reference time early for accuracy */
214190203Srpaulo    const uint64_t  core_num    = cvmx_get_core_num();  /* One timer per processor, so use this to select */
215190203Srpaulo
216190203Srpaulo    /* Make sure the specified time won't wrap our bucket list */
217190203Srpaulo    if (ticks_from_now > cvmx_tim.max_ticks)
218190203Srpaulo    {
219190203Srpaulo        cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too far away.\n");
220190203Srpaulo        return CVMX_TIM_STATUS_TOO_FAR_AWAY;
221190203Srpaulo    }
222190203Srpaulo
223190203Srpaulo    /* Since we have no way to synchronize, we can't update a timer that is
224190203Srpaulo        being used by the hardware. Two buckets forward should be safe */
225190203Srpaulo    if (ticks_from_now < 2)
226190203Srpaulo    {
227190203Srpaulo        cvmx_dprintf("cvmx_tim_add_entry: Tried to schedule work too soon. Delaying it.\n");
228190203Srpaulo        ticks_from_now = 2;
229190203Srpaulo    }
230190203Srpaulo
231190203Srpaulo    /* Get the bucket this work queue entry should be in. Remember the bucket
232190203Srpaulo        array is circular */
233190203Srpaulo    current_bucket = ((cycles - cvmx_tim.start_time)
234190203Srpaulo		   >> cvmx_tim.bucket_shift);
235190203Srpaulo    work_bucket = (((ticks_from_now * cvmx_tim.tick_cycles) + cycles - cvmx_tim.start_time)
236190203Srpaulo		   >> cvmx_tim.bucket_shift);
237190203Srpaulo
238190203Srpaulo    work_bucket_ptr = cvmx_tim.bucket + core_num * cvmx_tim.num_buckets + (work_bucket & (cvmx_tim.num_buckets - 1));
239190203Srpaulo    entries_per_chunk = (CVMX_FPA_TIMER_POOL_SIZE/8 - 1);
240190203Srpaulo
241190203Srpaulo    /* Check if we have room to add this entry into the existing list */
242190203Srpaulo    if (work_bucket_ptr->chunk_remainder)
243190203Srpaulo    {
244190203Srpaulo        /* Adding the work entry to the end of the existing list */
245190203Srpaulo        tim_entry_ptr = &(work_bucket_ptr->last_chunk->entries[entries_per_chunk - work_bucket_ptr->chunk_remainder]);
246190203Srpaulo        *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
247190203Srpaulo        work_bucket_ptr->chunk_remainder--;
248190203Srpaulo        work_bucket_ptr->num_entries++;
249190203Srpaulo    }
250190203Srpaulo    else
251190203Srpaulo    {
252190203Srpaulo        /* Current list is either completely empty or completely full. We need
253190203Srpaulo            to allocate a new chunk for storing this work entry */
254190203Srpaulo        cvmx_tim_entry_chunk_t *new_chunk = (cvmx_tim_entry_chunk_t *)cvmx_fpa_alloc(CVMX_FPA_TIMER_POOL);
255190203Srpaulo        if (new_chunk == NULL)
256190203Srpaulo        {
257190203Srpaulo            cvmx_dprintf("cvmx_tim_add_entry: Failed to allocate memory for new chunk.\n");
258190203Srpaulo            return CVMX_TIM_STATUS_NO_MEMORY;
259190203Srpaulo        }
260190203Srpaulo
261190203Srpaulo        /* Does a chunk currently exist? We have to check num_entries since
262190203Srpaulo            the hardware doesn't NULL out the chunk pointers on free */
263190203Srpaulo        if (work_bucket_ptr->num_entries)
264190203Srpaulo        {
265190203Srpaulo            /* This chunk must be appended to an existing list by putting
266190203Srpaulo            ** its address in the last spot of the existing chunk. */
267190203Srpaulo            work_bucket_ptr->last_chunk->entries[entries_per_chunk] = cvmx_ptr_to_phys(new_chunk);
268190203Srpaulo            work_bucket_ptr->num_entries++;
269190203Srpaulo        }
270190203Srpaulo        else
271190203Srpaulo        {
272190203Srpaulo            /* This is the very first chunk. Add it */
273190203Srpaulo            work_bucket_ptr->first_chunk_addr = cvmx_ptr_to_phys(new_chunk);
274190203Srpaulo            work_bucket_ptr->num_entries = 1;
275190203Srpaulo        }
276190203Srpaulo        work_bucket_ptr->last_chunk = new_chunk;
277190203Srpaulo        work_bucket_ptr->chunk_remainder = entries_per_chunk - 1;
278190203Srpaulo        tim_entry_ptr = &(new_chunk->entries[0]);
279190203Srpaulo        *tim_entry_ptr = cvmx_ptr_to_phys(work_entry);
280190203Srpaulo    }
281190203Srpaulo
282190203Srpaulo    /* If the user supplied a delete info structure then fill it in */
283190203Srpaulo    if (delete_info)
284190203Srpaulo    {
285190203Srpaulo        /* It would be very bad to delete a timer entry after, or during the
286190203Srpaulo            timer's processing. During the processing could yield unpredicatable
287190203Srpaulo            results, but after would always be bad. Modifying the entry after
288190203Srpaulo            processing means we would be changing data in a buffer that has been
289190203Srpaulo            freed, and possible allocated again. For this reason we store a
290190203Srpaulo            commit cycle count in the delete structure. If we are after this
291190203Srpaulo            count we will refuse to delete the timer entry. */
292190203Srpaulo        delete_info->commit_cycles = cycles + (ticks_from_now - 2) * cvmx_tim.tick_cycles;
293190203Srpaulo        delete_info->timer_entry_ptr = (uint64_t *)tim_entry_ptr;  /* Cast to non-volatile type */
294190203Srpaulo    }
295190203Srpaulo
296190203Srpaulo    CVMX_SYNCWS; /* Make sure the hardware timer unit can access valid data from L2 */
297190203Srpaulo
298190203Srpaulo    return CVMX_TIM_STATUS_SUCCESS;
299190203Srpaulo}
300190203Srpaulo#endif
301190203Srpaulo
302190203Srpaulo
303190203Srpaulo/**
304190203Srpaulo * Delete a timer entry scheduled using cvmx_tim_add_entry.
305190203Srpaulo * Deleting a timer will fail if it has already triggered or
306190203Srpaulo * might be in progress. The actual state of the work queue
307190203Srpaulo * entry isn't changed. You need to dispose of it properly.
308190203Srpaulo *
309190203Srpaulo * @param delete_info
310190203Srpaulo *               Structure passed to cvmx_tim_add_entry to store the
311190203Srpaulo *               information needed to delete a timer entry.
312190203Srpaulo * @return CVMX_TIM_STATUS_BUSY if the timer was not deleted, otherwise
313190203Srpaulo *         CVMX_TIM_STATUS_SUCCESS.
314190203Srpaulo */
315190203Srpaulostatic inline cvmx_tim_status_t cvmx_tim_delete_entry(cvmx_tim_delete_t *delete_info)
316190203Srpaulo{
317190203Srpaulo    const uint64_t cycles = cvmx_clock_get_count(CVMX_CLOCK_TIM);
318190203Srpaulo
319190203Srpaulo    if ((int64_t)(cycles - delete_info->commit_cycles) < 0)
320190203Srpaulo    {
321190203Srpaulo        /* Timer is far enough away. Safe to delete */
322190203Srpaulo        *delete_info->timer_entry_ptr = 0;
323190203Srpaulo        return CVMX_TIM_STATUS_SUCCESS;
324190203Srpaulo    }
325190203Srpaulo    else
326190203Srpaulo    {
327190203Srpaulo        /* Timer is passed the commit time. It cannot be stopped */
328190203Srpaulo        return CVMX_TIM_STATUS_BUSY;
329190203Srpaulo    }
330190203Srpaulo}
331190203Srpaulo
332190203Srpaulo#ifdef	__cplusplus
333190203Srpaulo}
334190203Srpaulo#endif
335190203Srpaulo
336190203Srpaulo#endif // __CVMX_TIM_H__
337190203Srpaulo