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