cvmx-tim.c revision 232812
1189251Ssam/***********************license start*************** 2189251Ssam * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights 3214734Srpaulo * reserved. 4189251Ssam * 5189251Ssam * 6189251Ssam * Redistribution and use in source and binary forms, with or without 7189251Ssam * modification, are permitted provided that the following conditions are 8189251Ssam * met: 9189251Ssam * 10189251Ssam * * Redistributions of source code must retain the above copyright 11189251Ssam * notice, this list of conditions and the following disclaimer. 12189251Ssam * 13189251Ssam * * Redistributions in binary form must reproduce the above 14189251Ssam * copyright notice, this list of conditions and the following 15189251Ssam * disclaimer in the documentation and/or other materials provided 16189251Ssam * with the distribution. 17189251Ssam 18214734Srpaulo * * Neither the name of Cavium Inc. nor the names of 19214734Srpaulo * its contributors may be used to endorse or promote products 20189251Ssam * derived from this software without specific prior written 21189251Ssam * permission. 22189251Ssam 23189251Ssam * This Software, including technical data, may be subject to U.S. export control 24189251Ssam * laws, including the U.S. Export Administration Act and its associated 25189251Ssam * regulations, and may be subject to export or import regulations in other 26189251Ssam * countries. 27189251Ssam 28189251Ssam * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29189251Ssam * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR 30189251Ssam * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31189251Ssam * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32189251Ssam * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33214734Srpaulo * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34214734Srpaulo * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35214734Srpaulo * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36214734Srpaulo * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37189251Ssam * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38189251Ssam ***********************license end**************************************/ 39189251Ssam 40189251Ssam 41189251Ssam 42189251Ssam 43189251Ssam 44189251Ssam 45214734Srpaulo 46189251Ssam/** 47189251Ssam * @file 48189251Ssam * 49189251Ssam * Support library for the hardware work queue timers. 50189251Ssam * 51189251Ssam * <hr>$Revision: 70030 $<hr> 52189251Ssam */ 53189251Ssam#include "executive-config.h" 54189251Ssam#include "cvmx-config.h" 55189251Ssam#include "cvmx.h" 56189251Ssam#include "cvmx-sysinfo.h" 57189251Ssam#include "cvmx-tim.h" 58189251Ssam#include "cvmx-bootmem.h" 59189251Ssam 60189251Ssam/* CSR typedefs have been moved to cvmx-tim-defs.h */ 61189251Ssam 62189251Ssam/** 63189251Ssam * Global structure holding the state of all timers. 64189251Ssam */ 65189251SsamCVMX_SHARED cvmx_tim_t cvmx_tim; 66189251Ssam 67189251Ssam 68189251Ssam#ifdef CVMX_ENABLE_TIMER_FUNCTIONS 69189251Ssam/** 70189251Ssam * Setup a timer for use. Must be called before the timer 71189251Ssam * can be used. 72189251Ssam * 73189251Ssam * @param tick Time between each bucket in microseconds. This must not be 74189251Ssam * smaller than 1024/(clock frequency in MHz). 75189251Ssam * @param max_ticks The maximum number of ticks the timer must be able 76189251Ssam * to schedule in the future. There are guaranteed to be enough 77189251Ssam * timer buckets such that: 78189251Ssam * number of buckets >= max_ticks. 79189251Ssam * @return Zero on success. Negative on error. Failures are possible 80189251Ssam * if the number of buckets needed is too large or memory 81189251Ssam * allocation fails for creating the buckets. 82189251Ssam */ 83189251Ssamint cvmx_tim_setup(uint64_t tick, uint64_t max_ticks) 84189251Ssam{ 85189251Ssam uint64_t timer_id; 86189251Ssam int error = -1; 87189251Ssam uint64_t tim_clock_hz = cvmx_clock_get_rate(CVMX_CLOCK_TIM); 88189251Ssam uint64_t hw_tick_ns; 89189251Ssam uint64_t hw_tick_ns_allowed; 90189251Ssam uint64_t tick_ns = 1000 * tick; 91189251Ssam int i; 92189251Ssam uint32_t temp; 93189251Ssam int timer_thr = 1024; 94189251Ssam 95189251Ssam /* for the simulator */ 96189251Ssam if (tim_clock_hz == 0) 97189251Ssam tim_clock_hz = 800000000; 98189251Ssam 99189251Ssam if (OCTEON_IS_MODEL(OCTEON_CN68XX)) 100189251Ssam { 101189251Ssam cvmx_tim_fr_rn_tt_t fr_tt; 102189251Ssam fr_tt.u64 = cvmx_read_csr(CVMX_TIM_FR_RN_TT); 103189251Ssam timer_thr = fr_tt.s.fr_rn_tt; 104189251Ssam } 105189251Ssam 106189251Ssam hw_tick_ns = timer_thr * 1000000000ull / tim_clock_hz; 107189251Ssam /* 108189251Ssam * Double the minimal allowed tick to 2 * HW tick. tick between 109189251Ssam * (hw_tick_ns, 2*hw_tick_ns) will set config_ring1.s.interval 110189251Ssam * to zero, or 1024 cycles. This is not enough time for the timer unit 111189251Ssam * to fetch the bucket data, Resulting in timer ring error interrupt 112189251Ssam * be always generated. Avoid such setting in software. 113189251Ssam */ 114189251Ssam hw_tick_ns_allowed = hw_tick_ns * 2; 115189251Ssam 116189251Ssam /* Make sure the timers are stopped */ 117189251Ssam cvmx_tim_stop(); 118189251Ssam 119189251Ssam /* Reinitialize out timer state */ 120189251Ssam memset(&cvmx_tim, 0, sizeof(cvmx_tim)); 121189251Ssam 122189251Ssam if (tick_ns < hw_tick_ns_allowed) 123189251Ssam { 124189251Ssam cvmx_dprintf("ERROR: cvmx_tim_setup: Requested tick %lu(ns) is smaller than" 125189251Ssam " the minimal ticks allowed by hardware %lu(ns)\n", 126189251Ssam tick_ns, hw_tick_ns_allowed); 127189251Ssam return error; 128189251Ssam } 129189251Ssam else if (tick_ns > 4194304 * hw_tick_ns) 130189251Ssam { 131189251Ssam cvmx_dprintf("ERROR: cvmx_tim_setup: Requested tick %lu(ns) is greater than" 132189251Ssam " the max ticks %lu(ns)\n", tick_ns, hw_tick_ns); 133189251Ssam return error; 134189251Ssam } 135189251Ssam 136189251Ssam for (i=2; i<20; i++) 137189251Ssam { 138189251Ssam if (tick_ns < (hw_tick_ns << i)) 139189251Ssam break; 140189251Ssam } 141189251Ssam 142189251Ssam cvmx_tim.max_ticks = (uint32_t)max_ticks; 143189251Ssam cvmx_tim.bucket_shift = (uint32_t)(i - 1 + 10); 144189251Ssam cvmx_tim.tick_cycles = tick * tim_clock_hz / 1000000; 145189251Ssam 146189251Ssam temp = (max_ticks * cvmx_tim.tick_cycles) >> cvmx_tim.bucket_shift; 147189251Ssam 148189251Ssam /* round up to nearest power of 2 */ 149189251Ssam temp -= 1; 150189251Ssam temp = temp | (temp >> 1); 151189251Ssam temp = temp | (temp >> 2); 152189251Ssam temp = temp | (temp >> 4); 153189251Ssam temp = temp | (temp >> 8); 154189251Ssam temp = temp | (temp >> 16); 155189251Ssam cvmx_tim.num_buckets = temp + 1; 156189251Ssam 157189251Ssam /* ensure input params fall into permitted ranges */ 158189251Ssam if ((cvmx_tim.num_buckets < 3) || cvmx_tim.num_buckets > 1048576) 159189251Ssam { 160189262Ssam cvmx_dprintf("ERROR: cvmx_tim_setup: num_buckets out of range\n"); 161189262Ssam return error; 162189262Ssam } 163189262Ssam 164214734Srpaulo /* Allocate the timer buckets from hardware addressable memory */ 165214734Srpaulo cvmx_tim.bucket = cvmx_bootmem_alloc(CVMX_TIM_NUM_TIMERS * cvmx_tim.num_buckets 166214734Srpaulo * sizeof(cvmx_tim_bucket_entry_t), CVMX_CACHE_LINE_SIZE); 167214734Srpaulo if (cvmx_tim.bucket == NULL) 168214734Srpaulo { 169214734Srpaulo cvmx_dprintf("ERROR: cvmx_tim_setup: allocation problem\n"); 170214734Srpaulo return error; 171214734Srpaulo } 172214734Srpaulo memset(cvmx_tim.bucket, 0, CVMX_TIM_NUM_TIMERS * cvmx_tim.num_buckets * sizeof(cvmx_tim_bucket_entry_t)); 173214734Srpaulo 174214734Srpaulo cvmx_tim.start_time = 0; 175214734Srpaulo 176214734Srpaulo /* Loop through all timers */ 177214734Srpaulo for (timer_id = 0; timer_id<CVMX_TIM_NUM_TIMERS; timer_id++) 178214734Srpaulo { 179214734Srpaulo int interval = ((1 << (cvmx_tim.bucket_shift - 10)) - 1); 180214734Srpaulo cvmx_tim_bucket_entry_t *bucket = cvmx_tim.bucket + timer_id * cvmx_tim.num_buckets; 181214734Srpaulo if (OCTEON_IS_MODEL(OCTEON_CN68XX)) 182214734Srpaulo { 183189251Ssam cvmx_tim_ringx_ctl0_t ring_ctl0; 184189251Ssam cvmx_tim_ringx_ctl1_t ring_ctl1; 185189251Ssam cvmx_tim_ringx_ctl2_t ring_ctl2; 186189251Ssam cvmx_tim_reg_flags_t reg_flags; 187189251Ssam 188189251Ssam /* Tell the hardware where about the bucket array */ 189189251Ssam ring_ctl2.u64 = 0; 190189251Ssam ring_ctl2.s.csize = CVMX_FPA_TIMER_POOL_SIZE / 8; 191189251Ssam ring_ctl2.s.base = cvmx_ptr_to_phys(bucket) >> 5; 192189251Ssam cvmx_write_csr(CVMX_TIM_RINGX_CTL2(timer_id), ring_ctl2.u64); 193189251Ssam 194189251Ssam reg_flags.u64 = cvmx_read_csr(CVMX_TIM_REG_FLAGS); 195214734Srpaulo ring_ctl1.u64 = 0; 196189251Ssam ring_ctl1.s.cpool = ((reg_flags.s.ena_dfb == 0) ? CVMX_FPA_TIMER_POOL : 0); 197189251Ssam ring_ctl1.s.bsize = cvmx_tim.num_buckets - 1; 198214734Srpaulo cvmx_write_csr(CVMX_TIM_RINGX_CTL1(timer_id), ring_ctl1.u64); 199189251Ssam 200189251Ssam ring_ctl0.u64 = 0; 201189251Ssam ring_ctl0.s.timercount = interval + timer_id * interval / CVMX_TIM_NUM_TIMERS; 202189251Ssam cvmx_write_csr(CVMX_TIM_RINGX_CTL0(timer_id), ring_ctl0.u64); 203189251Ssam 204189251Ssam ring_ctl0.u64 = cvmx_read_csr(CVMX_TIM_RINGX_CTL0(timer_id)); 205189251Ssam ring_ctl0.s.ena = 1; 206189251Ssam ring_ctl0.s.interval = interval; 207189251Ssam cvmx_write_csr(CVMX_TIM_RINGX_CTL0(timer_id), ring_ctl0.u64); 208189251Ssam ring_ctl0.u64 = cvmx_read_csr(CVMX_TIM_RINGX_CTL0(timer_id)); 209189251Ssam } 210189251Ssam else 211189251Ssam { 212189251Ssam cvmx_tim_mem_ring0_t config_ring0; 213189251Ssam cvmx_tim_mem_ring1_t config_ring1; 214189251Ssam /* Tell the hardware where about the bucket array */ 215189251Ssam config_ring0.u64 = 0; 216189251Ssam config_ring0.s.first_bucket = cvmx_ptr_to_phys(bucket) >> 5; 217189251Ssam config_ring0.s.num_buckets = cvmx_tim.num_buckets - 1; 218214734Srpaulo config_ring0.s.ring = timer_id; 219189251Ssam cvmx_write_csr(CVMX_TIM_MEM_RING0, config_ring0.u64); 220189251Ssam 221189251Ssam /* Tell the hardware the size of each chunk block in pointers */ 222189251Ssam config_ring1.u64 = 0; 223189251Ssam config_ring1.s.enable = 1; 224189251Ssam config_ring1.s.pool = CVMX_FPA_TIMER_POOL; 225189251Ssam config_ring1.s.words_per_chunk = CVMX_FPA_TIMER_POOL_SIZE / 8; 226189251Ssam config_ring1.s.interval = interval; 227189251Ssam config_ring1.s.ring = timer_id; 228189251Ssam cvmx_write_csr(CVMX_TIM_MEM_RING1, config_ring1.u64); 229189251Ssam } 230189251Ssam } 231189251Ssam 232189251Ssam return 0; 233189251Ssam} 234189251Ssam#endif 235189251Ssam 236189251Ssam/** 237189251Ssam * Start the hardware timer processing 238189251Ssam */ 239214734Srpaulovoid cvmx_tim_start(void) 240214734Srpaulo{ 241189251Ssam cvmx_tim_control_t control; 242189251Ssam 243189251Ssam control.u64 = cvmx_read_csr(CVMX_TIM_REG_FLAGS); 244189251Ssam control.s.enable_dwb = 1; 245189251Ssam control.s.enable_timers = 1; 246189251Ssam 247189251Ssam /* Remember when we started the timers */ 248189251Ssam cvmx_tim.start_time = cvmx_clock_get_count(CVMX_CLOCK_TIM); 249189251Ssam cvmx_write_csr(CVMX_TIM_REG_FLAGS, control.u64); 250189251Ssam} 251189251Ssam 252189251Ssam 253189251Ssam/** 254189251Ssam * Stop the hardware timer processing. Timers stay configured. 255189251Ssam */ 256189251Ssamvoid cvmx_tim_stop(void) 257189251Ssam{ 258189251Ssam cvmx_tim_control_t control; 259189251Ssam control.u64 = cvmx_read_csr(CVMX_TIM_REG_FLAGS); 260189251Ssam control.s.enable_dwb = 0; 261189251Ssam control.s.enable_timers = 0; 262214734Srpaulo cvmx_write_csr(CVMX_TIM_REG_FLAGS, control.u64); 263189251Ssam} 264189251Ssam 265189251Ssam 266189251Ssam/** 267189251Ssam * Stop the timer. After this the timer must be setup again 268189251Ssam * before use. 269189251Ssam */ 270189251Ssam#ifdef CVMX_ENABLE_TIMER_FUNCTIONS 271214734Srpaulovoid cvmx_tim_shutdown(void) 272214734Srpaulo{ 273189251Ssam uint32_t bucket; 274189251Ssam uint64_t timer_id; 275214734Srpaulo uint64_t entries_per_chunk; 276189251Ssam 277214734Srpaulo /* Make sure the timers are stopped */ 278189251Ssam cvmx_tim_stop(); 279189251Ssam 280189251Ssam entries_per_chunk = CVMX_FPA_TIMER_POOL_SIZE/8 - 1; 281189251Ssam 282189251Ssam /* Now walk all buckets freeing the chunks */ 283189251Ssam for (timer_id = 0; timer_id<CVMX_TIM_NUM_TIMERS; timer_id++) 284189251Ssam { 285189251Ssam for (bucket=0; bucket<cvmx_tim.num_buckets; bucket++) 286189251Ssam { 287214734Srpaulo uint64_t chunk_addr; 288214734Srpaulo uint64_t next_chunk_addr; 289214734Srpaulo cvmx_tim_bucket_entry_t *bucket_ptr = cvmx_tim.bucket + timer_id * cvmx_tim.num_buckets + bucket; 290214734Srpaulo CVMX_PREFETCH128(CAST64(bucket_ptr)); /* prefetch the next cacheline for future buckets */ 291189251Ssam 292189251Ssam /* Each bucket contains a list of chunks */ 293189251Ssam chunk_addr = bucket_ptr->first_chunk_addr; 294189251Ssam while (bucket_ptr->num_entries) 295189251Ssam { 296189251Ssam#ifdef DEBUG 297189251Ssam cvmx_dprintf("Freeing Timer Chunk 0x%llx\n", CAST64(chunk_addr)); 298189251Ssam#endif 299189251Ssam /* Read next chunk pointer from end of the current chunk */ 300189251Ssam next_chunk_addr = cvmx_read_csr(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, chunk_addr + CVMX_FPA_TIMER_POOL_SIZE - 8)); 301189251Ssam 302189251Ssam cvmx_fpa_free(cvmx_phys_to_ptr(chunk_addr), CVMX_FPA_TIMER_POOL, 0); 303189251Ssam chunk_addr = next_chunk_addr; 304189251Ssam if (bucket_ptr->num_entries > entries_per_chunk) 305189251Ssam bucket_ptr->num_entries -= entries_per_chunk; 306189251Ssam else 307189251Ssam bucket_ptr->num_entries = 0; 308189251Ssam } 309189251Ssam } 310189251Ssam } 311189251Ssam} 312189251Ssam 313189251Ssam#endif 314214734Srpaulo