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
34lpaddr_t platform_gic_cpu_interface_base = A9MPCORE_GIC_CPU_OFFSET;
35lpaddr_t platform_gic_distributor_base = A9MPCORE_GIC_DIST_OFFSET;
36
37/* These are for A9-specific devices, so are only used here. */
38
39static lpaddr_t
40platform_get_scu_address(void) {
41    assert(paging_mmu_enabled());
42    return platform_get_private_region() + A9MPCORE_SCU_OFFSET;
43}
44
45static lpaddr_t
46platform_get_gt_address(void) {
47    assert(paging_mmu_enabled());
48    return platform_get_private_region() + A9MPCORE_TIMER_GBL_OFFSET;
49}
50
51static lpaddr_t
52platform_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. */
58void
59platform_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 */
70size_t
71platform_get_core_count(void) {
72    return gic_cpu_count();
73}
74
75static cortex_a9_pit_t tsc;
76
77/* See TRM 4.3 */
78#define GLOBAL_TIMER_IRQ 27
79
80void 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
108bool 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
118uint32_t platform_get_timer_interrupt(void){
119    return GLOBAL_TIMER_IRQ;
120}
121
122systime_t systime_now(void)
123{
124    return a9_gt_read();
125}
126
127void systime_set_timeout(systime_t absolute_timeout)
128{
129    a9_gt_set_comparator(absolute_timeout);
130}
131
132void systime_set_timer(systime_t relative_timeout)
133{
134    systime_set_timeout(systime_now() + relative_timeout);
135}
136