cvmx-tim.h revision 210284
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 64extern "C" { 65#endif 66 67#define CVMX_TIM_NUM_TIMERS 16 68#define CVMX_TIM_NUM_BUCKETS 2048 69 70typedef 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 */ 88typedef 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 */ 99typedef 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 */ 115typedef 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 */ 135typedef 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 */ 145extern 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 */ 165int cvmx_tim_setup(uint64_t tick, uint64_t max_ticks); 166#endif 167 168/** 169 * Start the hardware timer processing 170 */ 171extern void cvmx_tim_start(void); 172 173 174/** 175 * Stop the hardware timer processing. Timers stay configured. 176 */ 177extern 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 185extern 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 */ 202static 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 */ 312static 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__ 334