aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/arm/arm32/mode_switch.S
blob: 2cd58880979fe370169440ebf45a625e902b14b5 (plain)
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
/*
 * xen/arch/arm/mode_switch.S
 *
 * Start-of day code to take a CPU from Secure mode to Hyp mode.
 *
 * Tim Deegan <tim@xen.org>
 * Copyright (c) 2011-2012 Citrix Systems.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <asm/config.h>
#include <asm/page.h>
#include <asm/platforms/vexpress.h>
#include <asm/platforms/exynos5.h>
#include <asm/asm_defns.h>
#include <asm/gic.h>

/* Wake up secondary cpus
 * This code relies on Machine ID and only works for Vexpress and the Arndale
 * TODO: Move this code either later (via platform specific desc) or in a bootwrapper
 * r5: Machine ID
 * Clobber r0 r2 */
GLOBAL(kick_cpus)
        ldr   r0, =MACH_TYPE_SMDK5250
        teq   r5, r0                          /* Are we running on the arndale? */
        beq   kick_cpus_arndale
        /* otherwise versatile express */
        /* write start paddr to v2m sysreg FLAGSSET register */
        ldr   r0, =(V2M_SYS_MMIO_BASE)        /* base V2M sysreg MMIO address */
        dsb
        mov   r2, #0xffffffff
        str   r2, [r0, #(V2M_SYS_FLAGSCLR)]
        dsb
        ldr   r2, =start
        add   r2, r2, r10
        str   r2, [r0, #(V2M_SYS_FLAGSSET)]
        dsb
        ldr   r2, =V2M_GIC_BASE_ADDRESS       /* r2 := VE gic base address */
        b     kick_cpus_sgi
kick_cpus_arndale:
        /* write start paddr to CPU 1 sysreg register */
        ldr   r0, =(S5P_PA_SYSRAM)
        ldr   r2, =start
        add   r2, r2, r10
        str   r2, [r0]
        dsb
        ldr   r2, =EXYNOS5_GIC_BASE_ADDRESS   /* r2 := Exynos5 gic base address */
kick_cpus_sgi:
        /* send an interrupt */
        ldr   r0, =GIC_DR_OFFSET              /* GIC distributor offset */
        add   r0, r2                          /* r0 := r0 + gic base address */
        mov   r2, #0x1
        str   r2, [r0, #(GICD_CTLR * 4)]      /* enable distributor */
        mov   r2, #0xfe0000
        str   r2, [r0, #(GICD_SGIR * 4)]      /* send IPI to everybody, SGI0 = Event check */
        dsb
        str   r2, [r0, #(GICD_CTLR * 4)]      /* disable distributor */
        mov   pc, lr


/* Get up a CPU into Hyp mode.  Clobbers r0-r3.
 *
 * r5: Machine ID
 * r12: CPU number
 *
 * This code is specific to the VE model/Arndale, and not intended to be used
 * on production systems.  As such it's a bit hackier than the main
 * boot code in head.S.  In future it will be replaced by better
 * integration with the bootloader/firmware so that Xen always starts
 * in Hyp mode.
 * Clobber r0 - r4 */
GLOBAL(enter_hyp_mode)
        mov   r3, lr                 /* Put return address in non-banked reg */
        cpsid aif, #0x16             /* Enter Monitor mode */
        mrc   CP32(r0, SCR)
        orr   r0, r0, #0x100         /* Set HCE */
        orr   r0, r0, #0xb1          /* Set SCD, AW, FW and NS */
        bic   r0, r0, #0xe           /* Clear EA, FIQ and IRQ */
        mcr   CP32(r0, SCR)

        ldr   r2, =MACH_TYPE_SMDK5250   /* r4 := Arndale machine ID */
        /* By default load Arndale defaults values */
        ldr   r0, =EXYNOS5_TIMER_FREQUENCY  /* r0 := timer's frequency */
        ldr   r1, =EXYNOS5_GIC_BASE_ADDRESS /* r1 := GIC base address */
        /* If it's not the Arndale machine ID, load VE values */
        teq   r5, r2
        ldrne r0, =V2M_TIMER_FREQUENCY
        ldrne r1, =V2M_GIC_BASE_ADDRESS

        /* Ugly: the system timer's frequency register is only
         * programmable in Secure state.  Since we don't know where its
         * memory-mapped control registers live, we can't find out the
         * right frequency. */
        mcr   CP32(r0, CNTFRQ)

        mrc   CP32(r0,NSACR)
        ldr   r4, =0x3fff            /* Allow access to all co-processors in NS mode */
        orr   r0, r0, r4
        orr   r0, r0, #(1<<18)       /* CA7/CA15: Allow access to ACTLR.SMP in NS mode */
        mcr   CP32(r0, NSACR)

        add   r0, r1, #GIC_DR_OFFSET
        /* Disable the GIC distributor, on the boot CPU only */
        mov   r4, #0
        teq   r12, #0                /* Is this the boot CPU? */
        streq r4, [r0]
        /* Continuing ugliness: Set up the GIC so NS state owns interrupts,
         * The first 32 interrupts (SGIs & PPIs) must be configured on all
         * CPUs while the remainder are SPIs and only need to be done one, on
         * the boot CPU. */
        add   r0, r0, #0x80          /* GICD_IGROUP0 */
        mov   r2, #0xffffffff        /* All interrupts to group 1 */
        str   r2, [r0]               /* Interrupts  0-31 (SGI & PPI) */
        teq   r12, #0                /* Boot CPU? */
        bne   skip_spis              /* Don't route SPIs on secondary CPUs */

        add   r4, r1, #GIC_DR_OFFSET
        ldr   r4, [r4, #4]            /* r4 := Interrupt Controller Type Reg */
        and   r4, r4, #GICD_TYPE_LINES /* r4 := number of SPIs */
1:      teq   r4, #0
        beq   skip_spis
        add   r0, r0, #4             /* Go to the new group */
        str   r2, [r0]               /* Update the group */
        sub  r4, r4, #1
        b     1b
skip_spis:
        /* Disable the GIC CPU interface on all processors */
        add   r0, r1, #GIC_CR_OFFSET
        mov   r1, #0
        str   r1, [r0]
        /* Must drop priority mask below 0x80 before entering NS state */
        ldr   r1, =0xff
        str   r1, [r0, #0x4]         /* -> GICC_PMR */
        /* Reset a few config registers */
        mov   r0, #0
        mcr   CP32(r0, FCSEIDR)
        mcr   CP32(r0, CONTEXTIDR)

        mrs   r0, cpsr               /* Copy the CPSR */
        add   r0, r0, #0x4           /* 0x16 (Monitor) -> 0x1a (Hyp) */
        msr   spsr_cxsf, r0          /* into the SPSR */
        movs  pc, r3                 /* Exception-return into Hyp mode */

/*
 * Local variables:
 * mode: ASM
 * indent-tabs-mode: nil
 * End:
 */