]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/arm/bsp/exynos/platform_control-arm-exynos.cpp
update
[l4.git] / kernel / fiasco / src / kern / arm / bsp / exynos / platform_control-arm-exynos.cpp
1 INTERFACE [arm && exynos]:
2
3 #include "types.h"
4 #include "io.h"
5 #include "mem_layout.h"
6 #include "mmio_register_block.h"
7
8 EXTENSION class Platform_control
9 {
10 public:
11
12   class Pmu : public Mmio_register_block
13   {
14   public:
15     explicit Pmu(Address virt) : Mmio_register_block(virt) {}
16     enum Reg
17     {
18       Config      = 0,
19       Status      = 4,
20       Option      = 8,
21       Core_offset = 0x80,
22
23       ARM_COMMON_OPTION      = 0x2408,
24    };
25
26     static Mword core_pwr_reg(Cpu_phys_id cpu, unsigned func)
27     { return 0x2000 + Core_offset * cxx::int_value<Cpu_phys_id>(cpu) + func; }
28
29     Mword read(unsigned reg) const
30     { return Mmio_register_block::read<Mword>(reg); }
31
32     void write(Mword val, unsigned reg) const
33     { Mmio_register_block::write(val, reg); }
34
35     Mword core_read(Cpu_phys_id cpu, unsigned reg) const
36     { return Mmio_register_block::read<Mword>(core_pwr_reg(cpu, reg)); }
37
38     void core_write(Mword val, Cpu_phys_id cpu, unsigned reg) const
39     { Mmio_register_block::write(val, core_pwr_reg(cpu, reg)); }
40   };
41
42   enum Power_down_mode
43   {
44     Aftr, Lpa, Dstop, Sleep,
45
46     Pwr_down_mode = Aftr,
47   };
48
49   enum Pmu_regs
50   {
51     Pmu_core_local_power_enable = 3,
52   };
53
54   static Static_object<Pmu> pmu;
55 };
56
57 //--------------------------------------------------------------------------
58 INTERFACE [arm && exynos && (exynos_extgic || exynos5) && cpu_suspend]:
59
60 #include "per_cpu_data.h"
61
62 EXTENSION class Platform_control
63 {
64 public:
65   static Per_cpu<bool> _suspend_allowed;
66 };
67
68 //--------------------------------------------------------------------------
69 IMPLEMENTATION [arm && exynos && !mp]:
70
71 PRIVATE static
72 int
73 Platform_control::power_up_core(Cpu_phys_id)
74 {
75   return -L4_err::ENodev;
76 }
77
78 //--------------------------------------------------------------------------
79 IMPLEMENTATION [arm && exynos]:
80
81 #include "per_cpu_data.h"
82
83 Static_object<Platform_control::Pmu> Platform_control::pmu;
84
85 //--------------------------------------------------------------------------
86 IMPLEMENTATION [arm && exynos && mp && cpu_suspend]:
87
88 #include "poll_timeout_kclock.h"
89
90 PRIVATE static
91 int
92 Platform_control::power_up_core(Cpu_phys_id cpu)
93 {
94   // CPU already powered up?
95   if ((pmu->core_read(cpu, Pmu::Status) & Pmu_core_local_power_enable) != 0)
96     return 0;
97
98   pmu->core_write(Pmu_core_local_power_enable, cpu, Pmu::Config);
99
100   Lock_guard<Cpu_lock, Lock_guard_inverse_policy> cpu_lock_guard(&cpu_lock);
101
102   Poll_timeout_kclock pt(10000);
103   while (pt.test((pmu->core_read(cpu, Pmu::Status)
104                   & Pmu_core_local_power_enable)
105                  != Pmu_core_local_power_enable))
106       ;
107
108   return pt.timed_out() ? -L4_err::ENodev : 0;
109 }
110
111 //--------------------------------------------------------------------------
112 IMPLEMENTATION [arm && exynos && mp && !cpu_suspend]:
113
114 PRIVATE static
115 int
116 Platform_control::power_up_core(Cpu_phys_id)
117 {
118   return 0;
119 }
120
121
122 //--------------------------------------------------------------------------
123 IMPLEMENTATION [arm && exynos]:
124
125 #include "kmem.h"
126 #include "mem_unit.h"
127 #include "outer_cache.h"
128
129 PRIVATE static
130 void
131 Platform_control::write_phys_mem_coherent(Mword addr_p, Mword value)
132 {
133   Mword addr_v = Kmem::mmio_remap(addr_p);
134   Io::write<Mword>(value, addr_v);
135   Mem_unit::flush_dcache((void *)addr_v, (void *)(addr_v + sizeof(value)));
136   Outer_cache::flush(addr_p);
137 }
138
139 //--------------------------------------------------------------------------
140 IMPLEMENTATION [arm && exynos && arm_em_ns && arm_smif_mc]:
141
142 PRIVATE static
143 void
144 Platform_control::cpuboot(Mword startup_vector, Cpu_phys_id cpu)
145 {
146   unsigned long b = Mem_layout::Sysram_phys_base;
147   if (Platform::is_5410())
148     b += 0x5301c;
149   else
150     b += Platform::is_4210() ? 0x1f01c : 0x2f01c;
151
152   if (Platform::is_4412())
153     b += cxx::int_value<Cpu_phys_id>(cpu) * 4;
154
155   write_phys_mem_coherent(b, startup_vector);
156   Exynos_smc::call(Exynos_smc::Cpu1boot, cxx::int_value<Cpu_phys_id>(cpu));
157 }
158
159 // ------------------------------------------------------------------------
160 IMPLEMENTATION [exynos && (!arm_em_ns || arm_smif_none)]:
161
162 #include "mem_layout.h"
163 #include "platform.h"
164
165 PRIVATE static
166 void
167 Platform_control::cpuboot(Mword startup_vector, Cpu_phys_id cpu)
168 {
169  unsigned long b = Mem_layout::Sysram_phys_base;
170
171   if (Platform::is_4210() && Platform::subrev() == 0x11)
172     b = Mem_layout::Pmu_phys_base + 0x814;
173   else if (Platform::is_4210() && Platform::subrev() == 0)
174     b += 0x5000;
175
176   if (Platform::is_4412())
177     b += cxx::int_value<Cpu_phys_id>(cpu) * 4;
178
179   write_phys_mem_coherent(b, startup_vector);
180 }
181
182 //--------------------------------------------------------------------------
183 IMPLEMENTATION [arm && exynos && mp]:
184
185 #include "ipi.h"
186 #include "kmem.h"
187 #include "mem_unit.h"
188 #include "outer_cache.h"
189 #include "platform.h"
190 #include "smc.h"
191
192 PUBLIC static
193 void
194 Platform_control::boot_ap_cpus(Address phys_reset_vector)
195 {
196   assert(current_cpu() == Cpu_number::boot_cpu());
197
198   if (Platform::is_4412() || Platform::is_5410())
199     {
200       for (Cpu_phys_id i = Cpu_phys_id(1);
201            i < Cpu_phys_id(4) && i < Cpu_phys_id(Config::Max_num_cpus);
202            ++i)
203         {
204           power_up_core(i);
205           if (Platform::is_4412())
206             cpuboot(phys_reset_vector, i);
207           Ipi::send(Ipi::Global_request, Cpu_number::boot_cpu(), i);
208         }
209
210       return;
211     }
212
213   Cpu_phys_id const second = Cpu_phys_id(1);
214   power_up_core(second);
215   cpuboot(phys_reset_vector, second);
216   Ipi::send(Ipi::Global_request, Cpu_number::boot_cpu(), second);
217 }
218
219
220 //--------------------------------------------------------------------------
221 IMPLEMENTATION [arm && ((exynos && !exynos_extgic && !exynos5 && cpu_suspend) || !cpu_suspend)]:
222
223 #include "l4_types.h"
224
225 PUBLIC static inline
226 bool
227 Platform_control::cpu_suspend_allowed(Cpu_number)
228 { return false; }
229
230 PUBLIC static
231 int
232 Platform_control::do_core_n_off(Cpu_number)
233 {
234   return -L4_err::EBusy;
235 }
236
237 //--------------------------------------------------------------------------
238 IMPLEMENTATION [arm && exynos && mp && (exynos_extgic || exynos5) && cpu_suspend]:
239
240 #include <cstdio>
241 #include "cpu.h"
242 #include "io.h"
243 #include "ipi.h"
244 #include "kmem.h"
245 #include "kmem_space.h"
246 #include "pic.h"
247 #include "processor.h"
248 #include "smc.h"
249
250 IMPLEMENT inline
251 bool
252 Platform_control::cpu_shutdown_available()
253 { return true; }
254
255 PRIVATE static inline
256 int
257 Platform_control::resume_cpu(Cpu_number cpu)
258 {
259   Cpu_phys_id const pcpu = Cpu::cpus.cpu(cpu).phys_id();
260   int r;
261   if ((r = power_up_core(pcpu)))
262     return r;
263
264   set_suspend_state(cpu, false);
265   extern char _tramp_mp_entry[];
266   cpuboot(Kmem_space::kdir()->virt_to_phys((Address)_tramp_mp_entry), pcpu);
267   Ipi::send(Ipi::Global_request, current_cpu(), pcpu);
268
269   return 0;
270 }
271
272 PRIVATE static inline
273 int
274 Platform_control::suspend_cpu(Cpu_number cpu)
275 {
276   set_suspend_state(cpu, true);
277   Ipi::send(Ipi::Global_request, current_cpu(), cpu);
278
279   return 0;
280 }
281
282 IMPLEMENT
283 int
284 Platform_control::cpu_allow_shutdown(Cpu_number cpu, bool allow)
285 {
286   if (allow && !Cpu::online(cpu))
287     suspend_cpu(cpu);
288   else if (!allow && Cpu::online(cpu))
289     resume_cpu(cpu);
290   else
291     return -L4_err::ENodev;
292
293   return 0;
294 }
295
296
297 //--------------------------------------------------------------------------
298 IMPLEMENTATION [arm && exynos && cpu_suspend && arm_em_ns]:
299
300 PRIVATE static inline
301 void
302 Platform_control::do_sleep()
303 {
304   // FIXME: I miss: Exynos_smc::cpusleep();
305   asm volatile("dsb; wfi" : : : "memory", "cc");
306 }
307
308 //--------------------------------------------------------------------------
309 IMPLEMENTATION [arm && exynos && cpu_suspend && !arm_em_ns]:
310
311 PRIVATE static inline
312 void
313 Platform_control::do_sleep()
314 {
315   asm volatile("dsb; wfi" : : : "memory", "cc");
316 }
317
318 //--------------------------------------------------------------------------
319 IMPLEMENTATION [arm && exynos && (exynos_extgic || exynos5) && cpu_suspend]:
320
321 #include <cassert>
322 #include <cstdio>
323 #include "cpu.h"
324 #include "io.h"
325 #include "kmem.h"
326 #include "pic.h"
327 #include "processor.h"
328
329 DEFINE_PER_CPU Per_cpu<bool> Platform_control::_suspend_allowed;
330
331 IMPLEMENT
332 void
333 Platform_control::init(Cpu_number cpu)
334 {
335   if (cpu == Cpu_number::boot_cpu())
336     {
337       assert (!pmu->get_mmio_base());
338       pmu.construct(Kmem::mmio_remap(Mem_layout::Pmu_phys_base));
339
340       for (Cpu_phys_id i = Cpu_phys_id(0);
341            i < Cpu_phys_id(2);
342            ++i)
343         pmu->core_write((pmu->core_read(i, Pmu::Option) & ~(1 << 0)) | (1 << 1), i, Pmu::Option);
344
345       pmu->write(2, Pmu::ARM_COMMON_OPTION);
346     }
347 }
348
349 PUBLIC static
350 void
351 Platform_control::set_suspend_state(Cpu_number cpu, bool state)
352 {
353   _suspend_allowed.cpu(cpu) = state;
354 }
355
356 PUBLIC static
357 bool
358 Platform_control::cpu_suspend_allowed(Cpu_number cpu)
359 {
360   return _suspend_allowed.cpu(cpu);
361 }
362
363 PUBLIC static
364 void
365 Platform_control::do_print_cpu_info(Cpu_phys_id cpu)
366 {
367   printf("fiasco: core%d: %lx/%lx/%lx\n", cxx::int_value<Cpu_phys_id>(cpu),
368          pmu->core_read(cpu, Pmu::Config),
369          pmu->core_read(cpu, Pmu::Status),
370          pmu->core_read(cpu, Pmu::Option));
371 }
372
373 PUBLIC static
374 int
375 Platform_control::do_core_n_off(Cpu_number cpu)
376 {
377   if (cpu == Cpu_number::boot_cpu())
378     return -L4_err::EBusy;
379
380   Cpu_phys_id const phys_cpu = Cpu::cpus.cpu(cpu).phys_id();
381
382   do_print_cpu_info(phys_cpu);
383
384   assert(cpu_lock.test()); // required for wfi
385
386   pmu->core_write(0, phys_cpu, Pmu::Config);
387
388   Mem_unit::flush_cache();
389   Mem_unit::tlb_flush();
390   Mem_unit::kernel_tlb_flush();
391
392   Cpu::disable_smp();
393   Cpu::disable_dcache();
394
395   do_sleep();
396
397   // we only reach here if the wfi was not done due to a pending event
398
399   Cpu::enable_dcache();
400   Cpu::enable_smp();
401
402   // todo: the timer irq needs a proper cpu setting here too
403   // (save + restore state)
404   Pic::reinit(cpu);
405
406   do_print_cpu_info(phys_cpu);
407
408   return 0;
409 }