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 event timer.
32 */
33
34#include <mach/mach_types.h>
35
36#include <kern/clock.h>
37#include <kern/thread.h>
38#include <kern/timer_queue.h>
39#include <kern/processor.h>
40#include <kern/macro_help.h>
41#include <kern/spl.h>
42#include <kern/etimer.h>
43#include <kern/pms.h>
44#include <kern/queue.h>
45
46#include <machine/commpage.h>
47#include <machine/machine_routines.h>
48
49#ifndef __LP64__
50
51/**
52 * timer_grab
53 *
54 * Return the raw timer value from the timer.
55 */
56uint64_t timer_grab(timer_t timer)
57{
58    return *(uint64_t *) timer;
59}
60#endif
61
62/**
63 * timer_update
64 *
65 * Update the raw timer value.
66 */
67void timer_update(timer_t timer, uint32_t new_high, uint32_t new_low)
68{
69    uint64_t *ptr = (uint64_t *) timer;
70    *ptr = ((uint64_t) (new_high) << 32) | new_low;
71}
72
73/**
74 * etimer_set_deadline
75 *
76 * Set the clock deadline.
77 */
78void etimer_set_deadline(uint64_t deadline)
79{
80    rtclock_timer_t *mytimer;
81    spl_t s;
82    cpu_data_t *pp;
83
84    s = splclock();             /* no interruptions */
85    pp = current_cpu_datap();
86
87    mytimer = &pp->rt_timer;    /* Point to the timer itself */
88    mytimer->deadline = deadline;   /* Set the new expiration time */
89
90    etimer_resync_deadlines();
91
92    splx(s);
93}
94
95/**
96 * etimer_resync_deadlines
97 *
98 * Resynchronize the timer deadlines, called from the interrupt routine.
99 */
100
101void etimer_resync_deadlines(void)
102{
103    uint64_t deadline;
104    uint64_t pmdeadline;
105    rtclock_timer_t *mytimer;
106    spl_t s = splclock();
107    cpu_data_t *pp;
108    uint32_t decr;
109
110    pp = current_cpu_datap();
111    deadline = EndOfAllTime;
112
113    /*
114     * If we have a clock timer set, pick that.
115     */
116    mytimer = &pp->rt_timer;
117    if (!mytimer->has_expired && 0 < mytimer->deadline
118        && mytimer->deadline < EndOfAllTime)
119        deadline = mytimer->deadline;
120
121    /*
122     * Go and set the "pop" event.
123     */
124
125    if (deadline > 0 && deadline <= pp->rtcPop) {
126        int decr;
127        uint64_t now;
128
129        now = mach_absolute_time();
130        decr = setPop(deadline);
131
132        if (deadline < now) {
133            pp->rtcPop = now + decr;
134        } else {
135            pp->rtcPop = deadline;
136        }
137    }
138
139    splx(s);
140}
141
142/**
143 * etimer_intr
144 *
145 * Timer interrupt routine, called from the realtime clock interrupt
146 * routine.
147 */
148void etimer_intr(int inuser, uint64_t iaddr)
149{
150    uint64_t abstime;
151    rtclock_timer_t *mytimer;
152    cpu_data_t *pp;
153    int32_t latency;
154
155    pp = current_cpu_datap();
156
157    SCHED_STATS_TIMER_POP(current_processor());
158
159    abstime = mach_absolute_time(); /* Get the time now */
160
161    /*
162     * has a pending clock timer expired?
163     */
164    mytimer = &pp->rt_timer;    /* Point to the event timer */
165    if (mytimer->deadline <= abstime) {
166        mytimer->has_expired = TRUE;    /* Remember that we popped */
167        mytimer->deadline = timer_queue_expire(&mytimer->queue, abstime);
168        mytimer->has_expired = FALSE;
169    }
170
171    pp->rtcPop = EndOfAllTime;  /* any real deadline will be earlier */
172    /*
173     * schedule our next deadline
174     */
175
176    etimer_resync_deadlines();
177}
178
179/**
180 * timer_queue_assign
181 *
182 * Assign a deadline and return the current processor's timer queue.
183 */
184mpqueue_head_t *timer_queue_assign(uint64_t deadline)
185{
186    cpu_data_t *cdp = current_cpu_datap();
187    mpqueue_head_t *queue;
188
189    if (cdp->cpu_running) {
190        queue = &cdp->rt_timer.queue;
191        if (deadline < cdp->rt_timer.deadline) {
192            etimer_set_deadline(deadline);
193        }
194    } else {
195        queue = &cpu_datap(master_cpu)->rt_timer.queue;
196    }
197
198    return (queue);
199}
200
201/**
202 * timer_call_slop
203 *
204 * Used for coalescing timer deadlines.
205 */
206uint64_t timer_call_slop(uint64_t deadline)
207{
208    uint64_t now = mach_absolute_time();
209    if (deadline > now) {
210        return MIN((deadline - now) >> 3, NSEC_PER_MSEC);   /* Min of 12.5% and 1ms */
211    }
212}
213
214/**
215 * timer_queue_cancel
216 *
217 * Remove a timer from the queue.
218 */
219void timer_queue_cancel(mpqueue_head_t * queue, uint64_t deadline,
220                        uint64_t new_deadline)
221{
222    if (queue == &current_cpu_datap()->rt_timer.queue) {
223        if (deadline < new_deadline) {
224            etimer_set_deadline(new_deadline);
225        }
226    }
227}
228