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