1/*
2 * Copyright 2013, winocm. <winocm@icloud.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *   Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 *
11 *   Redistributions in binary form must reproduce the above copyright notice, this
12 *   list of conditions and the following disclaimer in the documentation and/or
13 *   other materials provided with the distribution.
14 *
15 *   If you are going to use this software in any form that does not involve
16 *   releasing the source to this project or improving it, let me know beforehand.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 * ARM realtime clock.
32 */
33
34#include <platforms.h>
35
36#include <mach/mach_types.h>
37
38#include <kern/cpu_data.h>
39#include <kern/cpu_number.h>
40#include <kern/clock.h>
41#include <kern/host_notify.h>
42#include <kern/macro_help.h>
43#include <kern/misc_protos.h>
44#include <kern/spl.h>
45#include <kern/assert.h>
46#include <kern/etimer.h>
47#include <mach/vm_prot.h>
48#include <vm/pmap.h>
49#include <vm/vm_kern.h>         /* for kernel_map */
50#include <pexpert/pexpert.h>
51#include <machine/limits.h>
52#include <machine/commpage.h>
53#include <sys/kdebug.h>
54
55#include <arm/machine_cpu.h>
56
57#include <pexpert/pexpert.h>
58#include <pexpert/arm/boot.h>
59#include <pexpert/arm/protos.h>
60
61#include <libkern/OSBase.h>
62
63static uint32_t rtclock_sec_divisor;
64static uint64_t rtclock_scaler = 0;
65
66static uint64_t rtc_decrementer_min;
67static uint64_t rtc_decrementer_max;
68
69extern uint64_t clock_decrementer;
70
71#define UI_CPUFREQ_ROUNDING_FACTOR  10000000
72
73/* Decimal powers: */
74#define kilo (1000ULL)
75#define Mega (kilo * kilo)
76#define Giga (kilo * Mega)
77#define Tera (kilo * Giga)
78#define Peta (kilo * Tera)
79
80uint64_t tscFreq = 0, tscFCvtt2n, tscFCvtn2t;
81
82uint64_t rtclock_timer_scale_convert(void)
83{
84    uint64_t ticks;
85    uint64_t nsecs = pe_arm_get_timebase(NULL);
86
87    /* nanoseconds = (((rdtsc - rnt_tsc_base) * 10e9) / tscFreq) - rnt_ns_base; */
88    if(!tscFreq)
89        return 0;
90
91    ticks = (nsecs * NSEC_PER_SEC / tscFreq);
92    return ticks;
93}
94
95static uint64_t deadline_to_decrementer(uint64_t deadline, uint64_t now)
96{
97    uint64_t  delta;
98    if (deadline <= now)
99        return rtc_decrementer_min;
100    else {
101        delta = deadline - now;
102        return MIN(MAX(rtc_decrementer_min,delta),rtc_decrementer_max);
103    }
104}
105
106/*
107 * The units are a 64-bit fixed point integer which contains a high 32-bit integer part
108 * and a low 32-bit fractional part.
109 */
110uint64_t tmrCvt(uint64_t time, uint64_t unit)
111{
112    return ((time * unit) >> 32);
113}
114
115static uint64_t rtclock_internal_arm_timer(uint64_t deadline, uint64_t now)
116{
117    uint64_t current = pe_arm_get_timebase(NULL);
118    uint64_t set = 0;
119
120    if(deadline) {
121        uint64_t delta = deadline_to_decrementer(deadline, now);
122        uint64_t delta_abs;
123
124        set = now + delta;
125
126        /* Convert to Absolute frequency. */
127        delta_abs = tmrCvt(delta, tscFCvtn2t);
128
129#if CONFIG_TICKLESS
130        /* Set internal decrementer and rearm. */
131        clock_decrementer = delta_abs;
132#endif
133
134    } else {
135#if CONFIG_TICKLESS
136        printf("disarming timer...\n");
137        clock_decrementer = EndOfAllTime;
138#endif
139    }
140
141#if CONFIG_TICKLESS
142    /* Rearm decrementer by disable-reenable. This resets the timer. */
143    pe_arm_set_timer_enabled(FALSE);
144    pe_arm_set_timer_enabled(TRUE);
145#endif
146
147    return set;
148}
149
150int setPop(uint64_t time)
151{
152    uint64_t now;
153    uint64_t pop;
154
155    /*
156     * 0 and EndOfAllTime are special-cases for "clear the timer"
157     */
158    if (time == 0 || time == EndOfAllTime) {
159        time = EndOfAllTime;
160        now = 0;
161        pop = rtclock_internal_arm_timer(0, 0);
162    } else {
163        now = mach_absolute_time();       /* The time in nanoseconds */
164        pop = rtclock_internal_arm_timer(time, now);
165    }
166
167    /*
168     * Record requested and actual deadlines set
169     */
170    current_cpu_datap()->rtcPop = pop;
171
172    return (pop - now);
173}
174
175void rtclock_intr(arm_saved_state_t * regs)
176{
177    /*
178     * Interrupts must be enabled.
179     */
180
181    spl_t x = splclock();
182    etimer_intr(0, 0);
183    splx(x);
184
185    return;
186}
187
188uint64_t mach_absolute_time(void)
189{
190    if (!rtclock_sec_divisor)
191        return 0;
192    return rtclock_timer_scale_convert();
193}
194
195void machine_delay_until(uint64_t interval, uint64_t deadline)
196{
197    uint64_t now;
198
199    do {
200        cpu_pause();
201        now = mach_absolute_time();
202    } while (now < deadline);
203}
204
205static inline uint32_t
206_absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *microsecs)
207{
208    uint32_t remain;
209    *secs = abstime / (uint64_t)NSEC_PER_SEC;
210    remain = (uint32_t)(abstime % (uint64_t)NSEC_PER_SEC);
211    *microsecs = remain / NSEC_PER_USEC;
212    return remain;
213}
214
215static inline void
216_absolutetime_to_nanotime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *nanosecs)
217{
218    *secs = abstime / (uint64_t)NSEC_PER_SEC;
219    *nanosecs = (clock_usec_t)(abstime % (uint64_t)NSEC_PER_SEC);
220}
221
222void
223clock_interval_to_absolutetime_interval(uint32_t interval, uint32_t scale_factor, uint64_t* result)
224{
225    *result = (uint64_t)interval * scale_factor;
226}
227
228void
229absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *microsecs)
230{
231    _absolutetime_to_microtime(abstime, secs, microsecs);
232}
233
234void
235absolutetime_to_nanotime(uint64_t abstime, clock_sec_t* secs, clock_nsec_t *nanosecs)
236{
237    _absolutetime_to_nanotime(abstime, secs, nanosecs);
238}
239
240void
241nanotime_to_absolutetime(clock_sec_t secs, clock_nsec_t nanosecs, uint64_t* result)
242{
243    *result = ((uint64_t)secs * NSEC_PER_SEC) + nanosecs;
244}
245
246void
247absolutetime_to_nanoseconds(uint64_t abstime, uint64_t* result)
248{
249    *result = abstime;
250}
251
252void
253nanoseconds_to_absolutetime(uint64_t nanoseconds, uint64_t* result)
254{
255    *result = nanoseconds;
256}
257
258void clock_get_system_nanotime(clock_sec_t * secs, clock_nsec_t * nanosecs)
259{
260    uint64_t now = pe_arm_get_timebase(NULL);
261
262    _absolutetime_to_nanotime(now, secs, nanosecs);
263}
264
265void clock_get_system_microtime(clock_sec_t * secs, clock_usec_t * microsecs)
266{
267    uint64_t  now = mach_absolute_time();
268    _absolutetime_to_microtime(now, secs, microsecs);
269}
270
271int rtclock_config(void)
272{
273    return (1);
274}
275
276void rtc_configure(uint64_t hz)
277{
278    rtclock_sec_divisor = hz;
279    return;
280}
281
282void rtclock_bus_configure(void)
283{
284    uint64_t cycles;
285    kprintf("rtclock_bus_configure: Configuring RTCLOCK...\n");
286
287    /* We need a Hz, this will be specified in ...Hz. */
288    if(!gPEClockFrequencyInfo.timebase_frequency_hz)
289        panic("rtclock_bus_configure");
290
291    if(!rtclock_sec_divisor)
292        panic("rtclock_bus_configure 2");
293
294    cycles = gPEClockFrequencyInfo.timebase_frequency_hz;
295
296    /* Calculate 'TSC' frequency. */
297    tscFreq = cycles;
298    tscFCvtt2n = ((1 * Giga) << 32) / tscFreq;
299    tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n;
300
301    kprintf("[RTCLOCK] Frequency = %6d.%06dMHz, "
302        "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
303        (uint32_t)(tscFreq / Mega),
304        (uint32_t)(tscFreq % Mega),
305        (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n,
306        (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t);
307
308    rtc_decrementer_max = UINT64_MAX; /* No limit. */
309    rtc_decrementer_min = 1*NSEC_PER_USEC;  /* 1 usec */
310}
311
312int rtclock_init(void)
313{
314    uint64_t cycles;
315    assert(!ml_get_interrupts_enabled());
316
317    if(cpu_number() == master_cpu) {
318        pe_arm_init_timebase(NULL);
319        rtclock_bus_configure();
320
321        current_cpu_datap()->rtcPop = 0;
322        clock_timebase_init();
323    }
324
325    ml_init_lock_timeout();
326
327    return 1;
328}
329
330void clock_timebase_info(mach_timebase_info_t info)
331{
332    info->numer = info->denom = 1;
333}
334
335
336void clock_gettimeofday_set_commpage(uint64_t abstime, uint64_t epoch,
337                                     uint64_t offset, clock_sec_t * secs,
338                                     clock_usec_t * microsecs)
339{
340    uint64_t now = abstime + offset;
341    uint32_t remain;
342
343    remain = _absolutetime_to_microtime(now, secs, microsecs);
344    *secs += (clock_sec_t)epoch;
345    commpage_set_timestamp(abstime - remain, *secs, 0);
346}
347
348
349