cvmx-tim.c 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 * Support library for the hardware work queue timers. 48 * 49 * <hr>$Revision: 42180 $<hr> 50 */ 51#include "executive-config.h" 52#include "cvmx-config.h" 53#include "cvmx.h" 54#include "cvmx-sysinfo.h" 55#include "cvmx-tim.h" 56#include "cvmx-bootmem.h" 57 58/* CSR typedefs have been moved to cvmx-csr-*.h */ 59 60/** 61 * Global structure holding the state of all timers. 62 */ 63CVMX_SHARED cvmx_tim_t cvmx_tim; 64 65 66#ifdef CVMX_ENABLE_TIMER_FUNCTIONS 67/** 68 * Setup a timer for use. Must be called before the timer 69 * can be used. 70 * 71 * @param tick Time between each bucket in microseconds. This must not be 72 * smaller than 1024/(clock frequency in MHz). 73 * @param max_ticks The maximum number of ticks the timer must be able 74 * to schedule in the future. There are guaranteed to be enough 75 * timer buckets such that: 76 * number of buckets >= max_ticks. 77 * @return Zero on success. Negative on error. Failures are possible 78 * if the number of buckets needed is too large or memory 79 * allocation fails for creating the buckets. 80 */ 81int cvmx_tim_setup(uint64_t tick, uint64_t max_ticks) 82{ 83 cvmx_tim_mem_ring0_t config_ring0; 84 cvmx_tim_mem_ring1_t config_ring1; 85 uint64_t timer_id; 86 int error = -1; 87#if !(defined(__KERNEL__) && defined(linux)) 88 cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get(); 89 uint64_t cpu_clock_hz = sys_info_ptr->cpu_clock_hz; 90#else 91 uint64_t cpu_clock_hz = octeon_get_clock_rate(); 92#endif 93 uint64_t hw_tick_ns; 94 uint64_t hw_tick_ns_allowed; 95 uint64_t tick_ns = 1000 * tick; 96 int i; 97 uint32_t temp; 98 99 /* for the simulator */ 100 if (cpu_clock_hz == 0) 101 cpu_clock_hz = 333000000; 102 103 hw_tick_ns = 1024 * 1000000000ull / cpu_clock_hz; 104 /* 105 * Doulbe the minmal allowed tick to 2* HW tick. tick between 106 * (hw_tick_ns, 2*hw_tick_ns) will set config_ring1.s.interval 107 * to zero, or 1024 cycles. This is not enough time for the timer unit 108 * to fetch the bucket data, Resulting in timer ring error interrupt 109 * be always generated. Avoid such setting in software 110 */ 111 hw_tick_ns_allowed = hw_tick_ns *2; 112 113 /* Make sure the timers are stopped */ 114 cvmx_tim_stop(); 115 116 /* Reinitialize out timer state */ 117 memset(&cvmx_tim, 0, sizeof(cvmx_tim)); 118 119 120 if ((tick_ns < (hw_tick_ns_allowed)) || (tick_ns > 4194304 * hw_tick_ns)) 121 { 122 cvmx_dprintf("init: tick wrong size. Requested tick %lu(ns) is smaller than" 123 " the minimal ticks allowed by hardware %lu(ns)\n", 124 tick_ns, hw_tick_ns_allowed); 125 return error; 126 } 127 128 for (i=2; i<20; i++) 129 { 130 if (tick_ns < (hw_tick_ns << i)) 131 break; 132 } 133 134 cvmx_tim.max_ticks = (uint32_t)max_ticks; 135 cvmx_tim.bucket_shift = (uint32_t)(i - 1 + 10); 136 cvmx_tim.tick_cycles = tick * cpu_clock_hz / 1000000; 137 138 temp = (max_ticks * cvmx_tim.tick_cycles) >> cvmx_tim.bucket_shift; 139 140 /* round up to nearest power of 2 */ 141 temp -= 1; 142 temp = temp | (temp >> 1); 143 temp = temp | (temp >> 2); 144 temp = temp | (temp >> 4); 145 temp = temp | (temp >> 8); 146 temp = temp | (temp >> 16); 147 cvmx_tim.num_buckets = temp + 1; 148 149 /* ensure input params fall into permitted ranges */ 150 if ((cvmx_tim.num_buckets < 3) || cvmx_tim.num_buckets > 1048576) 151 { 152 cvmx_dprintf("init: num_buckets out of range\n"); 153 return error; 154 } 155 156 /* Allocate the timer buckets from hardware addressable memory */ 157 cvmx_tim.bucket = cvmx_bootmem_alloc(CVMX_TIM_NUM_TIMERS * cvmx_tim.num_buckets 158 * sizeof(cvmx_tim_bucket_entry_t), CVMX_CACHE_LINE_SIZE); 159 if (cvmx_tim.bucket == NULL) 160 { 161 cvmx_dprintf("init: allocation problem\n"); 162 return error; 163 } 164 memset(cvmx_tim.bucket, 0, CVMX_TIM_NUM_TIMERS * cvmx_tim.num_buckets * sizeof(cvmx_tim_bucket_entry_t)); 165 166 cvmx_tim.start_time = 0; 167 168 /* Loop through all timers */ 169 for (timer_id = 0; timer_id<CVMX_TIM_NUM_TIMERS; timer_id++) 170 { 171 cvmx_tim_bucket_entry_t *bucket = cvmx_tim.bucket + timer_id * cvmx_tim.num_buckets; 172 /* Tell the hardware where about the bucket array */ 173 config_ring0.u64 = 0; 174 config_ring0.s.first_bucket = cvmx_ptr_to_phys(bucket) >> 5; 175 config_ring0.s.num_buckets = cvmx_tim.num_buckets - 1; 176 config_ring0.s.ring = timer_id; 177 cvmx_write_csr(CVMX_TIM_MEM_RING0, config_ring0.u64); 178 179 /* Tell the hardware the size of each chunk block in pointers */ 180 config_ring1.u64 = 0; 181 config_ring1.s.enable = 1; 182 config_ring1.s.pool = CVMX_FPA_TIMER_POOL; 183 config_ring1.s.words_per_chunk = CVMX_FPA_TIMER_POOL_SIZE / 8; 184 config_ring1.s.interval = (1 << (cvmx_tim.bucket_shift - 10)) - 1; 185 config_ring1.s.ring = timer_id; 186 cvmx_write_csr(CVMX_TIM_MEM_RING1, config_ring1.u64); 187 } 188 189 return 0; 190} 191#endif 192 193/** 194 * Start the hardware timer processing 195 */ 196void cvmx_tim_start(void) 197{ 198 cvmx_tim_control_t control; 199 200 control.u64 = 0; 201 control.s.enable_dwb = 1; 202 control.s.enable_timers = 1; 203 204 /* Remember when we started the timers */ 205 cvmx_tim.start_time = cvmx_get_cycle(); 206 cvmx_write_csr(CVMX_TIM_REG_FLAGS, control.u64); 207} 208 209 210/** 211 * Stop the hardware timer processing. Timers stay configured. 212 */ 213void cvmx_tim_stop(void) 214{ 215 cvmx_tim_control_t control; 216 control.u64 = 0; 217 control.s.enable_dwb = 0; 218 control.s.enable_timers = 0; 219 cvmx_write_csr(CVMX_TIM_REG_FLAGS, control.u64); 220} 221 222 223/** 224 * Stop the timer. After this the timer must be setup again 225 * before use. 226 */ 227#ifdef CVMX_ENABLE_TIMER_FUNCTIONS 228void cvmx_tim_shutdown(void) 229{ 230 uint32_t bucket; 231 uint64_t timer_id; 232 uint64_t entries_per_chunk; 233 234 /* Make sure the timers are stopped */ 235 cvmx_tim_stop(); 236 237 entries_per_chunk = CVMX_FPA_TIMER_POOL_SIZE/8 - 1; 238 239 /* Now walk all buckets freeing the chunks */ 240 for (timer_id = 0; timer_id<CVMX_TIM_NUM_TIMERS; timer_id++) 241 { 242 for (bucket=0; bucket<cvmx_tim.num_buckets; bucket++) 243 { 244 uint64_t chunk_addr; 245 uint64_t next_chunk_addr; 246 cvmx_tim_bucket_entry_t *bucket_ptr = cvmx_tim.bucket + timer_id * cvmx_tim.num_buckets + bucket; 247 CVMX_PREFETCH128(CAST64(bucket_ptr)); /* prefetch the next cacheline for future buckets */ 248 249 /* Each bucket contains a list of chunks */ 250 chunk_addr = bucket_ptr->first_chunk_addr; 251 while (bucket_ptr->num_entries) 252 { 253#ifdef DEBUG 254 cvmx_dprintf("Freeing Timer Chunk 0x%llx\n", CAST64(chunk_addr)); 255#endif 256 /* Read next chunk pointer from end of the current chunk */ 257 next_chunk_addr = cvmx_read_csr(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, chunk_addr + CVMX_FPA_TIMER_POOL_SIZE - 8)); 258 259 cvmx_fpa_free(cvmx_phys_to_ptr(chunk_addr), CVMX_FPA_TIMER_POOL, 0); 260 chunk_addr = next_chunk_addr; 261 if (bucket_ptr->num_entries > entries_per_chunk) 262 bucket_ptr->num_entries -= entries_per_chunk; 263 else 264 bucket_ptr->num_entries = 0; 265 } 266 } 267 } 268} 269 270#endif 271