summaryrefslogtreecommitdiff
path: root/big-little/lib/ipi.c
blob: 936cda84d994adbd504d2d00838839aaebc0ba0a (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
/*
 * Copyright (c) 2012, ARM Limited. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that the
 * following conditions are met:
 *
 * Redistributions of source code must retain the above
 * copyright notice, this list of conditions and the
 * following disclaimer.
 *
 * Redistributions in binary form must reproduce the
 * above copyright notice, this list of conditions and
 * the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of ARM nor the names of its
 * contributors may be used to endorse or promote products
 * derived from this software without specific prior written
 * permission.
 */

#include "ipi.h"
#include "misc.h"
#include "virt_helpers.h"
#include "bakery.h"
#include "hyp_vmmap.h"
#include "gic_registers.h"

extern void gic_send_ipi(unsigned, unsigned);

/*
 * Set of flags used by the interrupt handling code
 * to distinguish between IPIs sent by the big-little
 * code and the payload software.
 * TODO: Assumes only one cpu will send an IPI at a
 * time rather than multiple cpus sending the same
 * IPI to each other at the same time from within the
 * HYP mode.
 */
static unsigned lock_ipi_check;
static unsigned hyp_ipi_check[16];

/*
 * Returns the id of the first IPI that is not pending on
 * our cpu interface or the first IPI that is pending but
 * was not generated by us. Returns 16 if no such IPI is
 * found
 */
static unsigned get_free_ipi(void)
{
        unsigned shift, cpu_if_bit, cpu_id = read_cpuid(), cluster_id =
                read_clusterid();
        int ctr;

        cpu_if_bit = 1 << get_cpuif(cluster_id, cpu_id);

        /* Find the register offset */
        for (ctr = 3; ctr >= 0; ctr--)
                /* Check whether IPI<shift> has already been generated by us */
                for (shift = 0; shift < 4; shift++) {
                        if (read32
                            (GIC_ID_PHY_BASE + GICD_SPENDSGIR +
                             (ctr << 2)) & (cpu_if_bit << (shift << 3)))
                                continue;

                        return (ctr << 2) + shift;
                }

        return MAX_IPI;
}

/*
 * Given a mask of destination cpus and what the IPI is supposed
 * todo, select an unused IPI and send it.
 */
unsigned send_hyp_ipi(unsigned cpuif_mask, unsigned type)
{
        unsigned rc = TRUE;
        unsigned ipi_no = 0;

        /*
         * First choose a non-pending IPI to avoid a clash with the OS.
         */
        ipi_no = get_free_ipi();
        if (ipi_no == MAX_IPI) {
                rc = FALSE;
                return rc;
        }

        /*
         * For this IPI set the mask in our global variable. We do it, payload software
         * does not. But, first check whether any earlier IPIs have already been acked
         */
        while (TRUE) {
                spin_lock(&lock_ipi_check);
                if (hyp_ipi_check[ipi_no] & 0xff) {
                        spin_unlock(&lock_ipi_check);
                } else {
                        hyp_ipi_check[ipi_no] = (type << 8) | cpuif_mask;
                        dsb();
                        spin_unlock(&lock_ipi_check);
                        break;
                }
        };

        /* Send the IPI to the cpu_mask */
        gic_send_ipi(cpuif_mask, ipi_no);

        return rc;
}

/*
 * Given a received IPI, check whether it is a HYP IPI and return
 * the type if so else 0
 */
unsigned get_hyp_ipi(unsigned cpu_if, unsigned ipi_no)
{
        unsigned type = 0;

        spin_lock(&lock_ipi_check);
        /*
         * If this IPI was sent by the big-little code then our cpu_if bit must have
         * been set in the ipi_check flag. Reset the bit and indicate that its an
         * internal IPI.
         */
        if (hyp_ipi_check[ipi_no] & (1 << cpu_if)) {
                type = hyp_ipi_check[ipi_no] >> 8;
                hyp_ipi_check[ipi_no] &= ~(1 << cpu_if);
                dsb();
        }
        spin_unlock(&lock_ipi_check);

        return type;
}