25c2252a98e0eb0a49f3140de423362b5ecb8cac
[barrelfish] / kernel / arch / armv7 / plat_a9mpcore.c
1 /**
2  * \file
3  * \brief Platform code for the Cortex-A9 MPCore.
4  */
5
6 /*
7  * Copyright (c) 2016 ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <maps/a9mpcore_map.h>
16 #include <a9_scu.h>
17 #include <a9_gt.h>
18 #include <gic.h>
19 #include <dev/cortex_a9_pit_dev.h>
20 #include <assert.h>
21 #include <cp15.h>
22 #include <kernel.h>
23 #include <init.h>
24 #include <paging_kernel_arch.h>
25 #include <arch/arm/platform.h>
26 #include <systime.h>
27 #include <arch/armv7/irq.h>
28
29 #define MSG(format, ...) \
30     printk( LOG_NOTE, "CortexA9 platform: "format, ## __VA_ARGS__ )
31
32 /* These are called from the A9/A15 common GIC (interrupt controller) code. */
33
34 lpaddr_t platform_gic_cpu_interface_base = A9MPCORE_GIC_CPU_OFFSET;
35 lpaddr_t platform_gic_distributor_base = A9MPCORE_GIC_DIST_OFFSET;
36
37 /* These are for A9-specific devices, so are only used here. */
38
39 static lpaddr_t
40 platform_get_scu_address(void) {
41     assert(paging_mmu_enabled());
42     return platform_get_private_region() + A9MPCORE_SCU_OFFSET;
43 }
44
45 static lpaddr_t
46 platform_get_gt_address(void) {
47     assert(paging_mmu_enabled());
48     return platform_get_private_region() + A9MPCORE_TIMER_GBL_OFFSET;
49 }
50
51 static lpaddr_t
52 platform_get_lt_address(void) {
53     assert(paging_mmu_enabled());
54     return platform_get_private_region() + A9MPCORE_TIMER_LCL_OFFSET;
55 }
56
57 /* On the A9, we need to initialise the snoop control unit. */
58 void
59 platform_revision_init(void) {
60     assert(paging_mmu_enabled());
61     a9_scu_init(platform_get_scu_address());
62     platform_gic_cpu_interface_base += platform_get_private_region();
63     platform_gic_distributor_base += platform_get_private_region();
64     if(platform_get_core_count() > 1) a9_scu_enable();
65 }
66
67 /*
68  * Return the core count from the interrupt controller
69  */
70 size_t
71 platform_get_core_count(void) {
72     return gic_cpu_count();
73 }
74
75 static cortex_a9_pit_t tsc;
76
77 /* See TRM 4.3 */
78 #define GLOBAL_TIMER_IRQ 27
79
80 void platform_timer_init(int timeslice)
81 {
82     errval_t err;
83     /* Time slice counter: use the Cortex-A9 Local Timer
84        (see Cortex-A9 MPCore TRM 4.1). */
85     lvaddr_t lcl_base =
86         paging_map_device(platform_get_lt_address(), A9MPCORE_TIMER_LCL_SIZE);
87     cortex_a9_pit_initialize(&tsc, (mackerel_addr_t)lcl_base);
88
89     /* Global timer: use the Cortex-A9 Global Timer
90        (see Cortex-A9 MPCore TRM 4.3). */
91     a9_gt_init(platform_get_gt_address());
92     err = platform_enable_interrupt(GLOBAL_TIMER_IRQ, 0, 0, 0);
93     if(err_is_fail(err)){
94         printk(LOG_ERR,
95                 "Failed to enable timer interrupt. Will continue without...");
96     }
97     /* Discover the clock rate. */
98     a9_probe_tsc();
99     assert(systime_frequency != 0);
100
101     MSG("System counter frequency is %lluHz.\n", systime_frequency);
102     /* Set kernel timeslice value, timeslice is in ms. */
103     kernel_timeslice = ns_to_systime(timeslice * 1000000);
104     MSG("Timeslice interrupt every %llu system ticks (%dms).\n", kernel_timeslice, timeslice);
105     systime_set_timer(kernel_timeslice);
106 }
107
108 bool platform_is_timer_interrupt(uint32_t irq)
109 {
110     if (irq == GLOBAL_TIMER_IRQ) {
111         a9_gt_ack_irq();
112         return 1;
113     }
114
115     return 0;
116 }
117
118 systime_t systime_now(void)
119 {
120     return a9_gt_read();
121 }
122
123 void systime_set_timeout(systime_t absolute_timeout)
124 {
125     a9_gt_set_comparator(absolute_timeout);
126 }
127
128 void systime_set_timer(systime_t relative_timeout)
129 {
130     systime_set_timeout(systime_now() + relative_timeout);
131 }