hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * SPDX-License-Identifier: GPL-2.0
 *
 * Copyright (C) 2001,2002,2003,2007,2012 Philippe Gerum <rpm@xenomai.org>.
 * Copyright (C) 2004 Gilles Chanteperdrix <gilles.chanteperdrix@xenomai.org>
 */
 
#include <linux/tick.h>
#include <linux/clockchips.h>
#include <cobalt/kernel/intr.h>
#include <pipeline/tick.h>
#include <cobalt/kernel/sched.h>
#include <cobalt/kernel/timer.h>
 
static DEFINE_PER_CPU(struct clock_proxy_device *, proxy_device);
 
const char *pipeline_timer_name(void)
{
   struct clock_proxy_device *dev = per_cpu(proxy_device, 0);
   struct clock_event_device *real_dev = dev->real_device;
 
   /*
    * Return the name of the current clock event chip, which is
    * the real device controlled by the proxy tick device.
    */
   return real_dev->name;
}
 
void pipeline_set_timer_shot(unsigned long delay) /* ns */
{
   struct clock_proxy_device *dev = __this_cpu_read(proxy_device);
   struct clock_event_device *real_dev = dev->real_device;
   u64 cycles;
   ktime_t t;
   int ret;
 
   if (real_dev->features & CLOCK_EVT_FEAT_KTIME) {
       t = ktime_add(delay, xnclock_core_read_raw());
       real_dev->set_next_ktime(t, real_dev);
   } else {
       if (delay <= 0) {
           delay = real_dev->min_delta_ns;
       } else {
           delay = min_t(int64_t, delay,
               real_dev->max_delta_ns);
           delay = max_t(int64_t, delay,
               real_dev->min_delta_ns);
       }
       cycles = ((u64)delay * real_dev->mult) >> real_dev->shift;
       ret = real_dev->set_next_event(cycles, real_dev);
       if (ret)
           real_dev->set_next_event(real_dev->min_delta_ticks,
                       real_dev);
   }
}
 
static int proxy_set_next_ktime(ktime_t expires,
               struct clock_event_device *proxy_dev) /* hard irqs on/off */
{
   struct xnsched *sched;
   unsigned long flags;
   ktime_t delta;
   int ret;
 
   /*
    * Expiration dates of in-band timers are based on the common
    * monotonic time base. If the timeout date has already
    * elapsed, make sure xntimer_start() does not fail with
    * -ETIMEDOUT but programs the hardware for ticking
    * immediately instead.
    */
   delta = ktime_sub(expires, ktime_get());
   if (delta < 0)
       delta = 0;
 
   xnlock_get_irqsave(&nklock, flags);
   sched = xnsched_current();
   ret = xntimer_start(&sched->htimer, delta, XN_INFINITE, XN_RELATIVE);
   xnlock_put_irqrestore(&nklock, flags);
 
   return ret ? -ETIME : 0;
}
 
bool pipeline_must_force_program_tick(struct xnsched *sched)
{
   return sched->lflags & XNTSTOP;
}
 
static int proxy_set_oneshot_stopped(struct clock_event_device *proxy_dev)
{
   struct clock_event_device *real_dev;
   struct clock_proxy_device *dev;
   struct xnsched *sched;
   spl_t s;
 
   dev = container_of(proxy_dev, struct clock_proxy_device, proxy_device);
 
   /*
    * In-band wants to disable the clock hardware on entering a
    * tickless state, so we have to stop our in-band tick
    * emulation. Propagate the request for shutting down the
    * hardware to the real device only if we have no outstanding
    * OOB timers. CAUTION: the in-band timer is counted when
    * assessing the RQ_IDLE condition, so we need to stop it
    * prior to testing the latter.
    */
   xnlock_get_irqsave(&nklock, s);
   sched = xnsched_current();
   xntimer_stop(&sched->htimer);
   sched->lflags |= XNTSTOP;
 
   if (sched->lflags & XNIDLE) {
       real_dev = dev->real_device;
       real_dev->set_state_oneshot_stopped(real_dev);
   }
 
   xnlock_put_irqrestore(&nklock, s);
 
   return 0;
}
 
static void setup_proxy(struct clock_proxy_device *dev)
{
   struct clock_event_device *proxy_dev = &dev->proxy_device;
 
   dev->handle_oob_event = (typeof(dev->handle_oob_event))
       xnintr_core_clock_handler;
   proxy_dev->features |= CLOCK_EVT_FEAT_KTIME;
   proxy_dev->set_next_ktime = proxy_set_next_ktime;
   if (proxy_dev->set_state_oneshot_stopped)
       proxy_dev->set_state_oneshot_stopped = proxy_set_oneshot_stopped;
   __this_cpu_write(proxy_device, dev);
}
 
#ifdef CONFIG_SMP
static irqreturn_t tick_ipi_handler(int irq, void *dev_id)
{
   xnintr_core_clock_handler();
 
   return IRQ_HANDLED;
}
#endif
 
int pipeline_install_tick_proxy(void)
{
   int ret;
 
#ifdef CONFIG_SMP
   /*
    * We may be running a SMP kernel on a uniprocessor machine
    * whose interrupt controller provides no IPI: attempt to hook
    * the timer IPI only if the hardware can support multiple
    * CPUs.
    */
   if (num_possible_cpus() > 1) {
       ret = __request_percpu_irq(TIMER_OOB_IPI,
                   tick_ipi_handler,
                   IRQF_OOB, "Xenomai timer IPI",
                   &cobalt_machine_cpudata);
       if (ret)
           return ret;
   }
#endif
 
   /* Install the proxy tick device */
   ret = tick_install_proxy(setup_proxy, &xnsched_realtime_cpus);
   if (ret)
       goto fail_proxy;
 
   return 0;
 
fail_proxy:
#ifdef CONFIG_SMP
   if (num_possible_cpus() > 1)
       free_percpu_irq(TIMER_OOB_IPI, &cobalt_machine_cpudata);
#endif
 
   return ret;
}
 
void pipeline_uninstall_tick_proxy(void)
{
   /* Uninstall the proxy tick device. */
   tick_uninstall_proxy(&xnsched_realtime_cpus);
 
#ifdef CONFIG_SMP
   if (num_possible_cpus() > 1)
       free_percpu_irq(TIMER_OOB_IPI, &cobalt_machine_cpudata);
#endif
}