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