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