1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * This software may be distributed and modified according to the terms of
5 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
6 * See "LICENSE_GPLv2.txt" for details.
7 *
8 * @TAG(GD_GPL)
9 */
10
11#include <config.h>
12
13#include <linker.h>
14#include <machine/io.h>
15#include <plat/machine/hardware.h>
16#include <plat/machine/ioapic.h>
17
18#define IOAPIC_REGSEL 0x00
19#define IOAPIC_WINDOW 0x10
20
21#define IOAPIC_REG_IOAPICID 0x00
22#define IOAPIC_REG_IOREDTBL 0x10
23
24#define IOREDTBL_LOW(reg) (IOAPIC_REG_IOREDTBL + (reg) * 2)
25#define IOREDTBL_HIGH(reg) (IOREDTBL_LOW(reg) + 1)
26
27#define IOREDTBL_LOW_INTERRUPT_MASK BIT(16)
28#define IOREDTBL_LOW_TRIGGER_MODE_LEVEL BIT(15)
29#define IOREDTBL_LOW_TRIGGER_MODE_SHIFT    15
30#define IOREDTBL_LOW_POLARITY_LOW BIT(13)
31#define IOREDTBL_LOW_POLARITY_SHIFT         13
32#define IOREDTBL_LOW_DEST_MODE_LOGCIAL BIT(11)
33
34#define IOAPICID_ID_BITS 4
35#define IOAPICID_ID_OFFSET 24
36
37#define IOREDTBL_HIGH_RESERVED_BITS 24
38
39/* Cache what we believe is in the low word of the IOREDTBL. This
40 * has all the state of trigger modes etc etc */
41static uint32_t ioredtbl_state[IOAPIC_IRQ_LINES * CONFIG_MAX_NUM_IOAPIC];
42
43/* Number of IOAPICs in the system */
44static uint32_t num_ioapics = 0;
45
46static void ioapic_write(uint32_t ioapic, word_t reg, uint32_t value)
47{
48    *(volatile uint32_t*)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg) = value;
49}
50
51static uint32_t ioapic_read(uint32_t ioapic, word_t reg)
52{
53    return *(volatile uint32_t*)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg);
54}
55
56static void single_ioapic_init(word_t ioapic, cpu_id_t delivery_cpu)
57{
58    uint32_t i;
59
60    /* Mask all the IRQs. In doing so we happen to set
61     * the vector to 0, which we can assert against in
62     * mask_interrupt to ensure a vector is assigned
63     * before we unmask */
64    for (i = 0; i < IOAPIC_IRQ_LINES; i++) {
65        /* Send to desired cpu */
66        ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(i));
67        ioapic_write(ioapic, IOAPIC_WINDOW, (ioapic_read(ioapic, IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS)) | (delivery_cpu << IOREDTBL_HIGH_RESERVED_BITS));
68        /* mask and set 0 vector */
69        ioredtbl_state[i] = IOREDTBL_LOW_INTERRUPT_MASK;
70        ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(i));
71        /* The upper 16 bits are reserved, so we make sure to preserve them */
72        ioredtbl_state[i] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
73        ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[i]);
74    }
75}
76
77static  cpu_id_t ioapic_target_cpu = 0;
78void ioapic_init(uint32_t num_nodes, cpu_id_t *cpu_list, uint32_t num_ioapic)
79{
80    uint32_t ioapic;
81    num_ioapics = num_ioapic;
82    ioapic_target_cpu = cpu_list[0];
83
84    for (ioapic = 0; ioapic < num_ioapic; ioapic++) {
85        /* Init this ioapic */
86        single_ioapic_init(ioapic, cpu_list[0]);
87    }
88}
89
90void ioapic_mask(bool_t mask, uint32_t ioapic, uint32_t pin)
91{
92    int index = ioapic * IOAPIC_IRQ_LINES + pin;
93    if (ioapic >= num_ioapics || pin >= IOAPIC_IRQ_LINES) {
94        /* silently ignore requests to non existent parts of the interrupt space */
95        return;
96    }
97    if (mask) {
98        ioredtbl_state[index] |= IOREDTBL_LOW_INTERRUPT_MASK;
99    } else {
100        ioredtbl_state[index] &= ~IOREDTBL_LOW_INTERRUPT_MASK;
101        /* it should not be possible to be unmasking an interrupt, without
102         * it having been mapped to a vector, assert that this is the case */
103        assert((ioredtbl_state[index] & 0xff) != 0);
104    }
105    ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
106    ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
107}
108
109exception_t ioapic_decode_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
110                                            word_t polarity, word_t vector)
111{
112    if (num_ioapics == 0) {
113        userError("System has no IOAPICs");
114        current_syscall_error.type = seL4_IllegalOperation;
115        return EXCEPTION_SYSCALL_ERROR;
116    }
117    if (ioapic >= num_ioapics) {
118        userError("Invalid IOAPIC %ld, only have %ld", (long)ioapic, (long)num_ioapics);
119        current_syscall_error.type = seL4_RangeError;
120        current_syscall_error.rangeErrorMin = 0;
121        current_syscall_error.rangeErrorMax = num_ioapics - 1;
122        return EXCEPTION_SYSCALL_ERROR;
123    }
124    if (pin >= IOAPIC_IRQ_LINES) {
125        userError("Invalid IOAPIC pin %ld, there are %d pins", (long)pin, IOAPIC_IRQ_LINES);
126        current_syscall_error.type = seL4_RangeError;
127        current_syscall_error.rangeErrorMin = 0;
128        current_syscall_error.rangeErrorMax = IOAPIC_IRQ_LINES - 1;
129        return EXCEPTION_SYSCALL_ERROR;
130    }
131
132    if (level != 0 && level != 1) {
133        userError("Level should be 0 or 1, not %d", (int)level);
134        current_syscall_error.type = seL4_RangeError;
135        current_syscall_error.rangeErrorMin = 0;
136        current_syscall_error.rangeErrorMax = 1;
137        return EXCEPTION_SYSCALL_ERROR;
138    }
139    if (polarity != 0 && polarity != 1) {
140        userError("Polarity should be 0 or 1, not %d", (int)polarity);
141        current_syscall_error.type = seL4_RangeError;
142        current_syscall_error.rangeErrorMin = 0;
143        current_syscall_error.rangeErrorMax = 1;
144        return EXCEPTION_SYSCALL_ERROR;
145    }
146    return EXCEPTION_NONE;
147}
148
149void ioapic_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
150                              word_t polarity, word_t vector)
151{
152    uint32_t ioredtbl_high = 0;
153    uint32_t index = 0;
154
155    index = ioapic * IOAPIC_IRQ_LINES + pin;
156    ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(pin));
157    ioredtbl_high = ioapic_read(ioapic, IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS);
158    /* delivery mode: physical mode only, using APIC ID */
159    ioredtbl_high |= (ioapic_target_cpu << IOREDTBL_HIGH_RESERVED_BITS);
160    ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_high);
161    /* we do not need to add IRQ_INT_OFFSET to the vector here */
162    ioredtbl_state[index] = IOREDTBL_LOW_INTERRUPT_MASK |
163                            (level << IOREDTBL_LOW_TRIGGER_MODE_SHIFT) |
164                            (polarity << IOREDTBL_LOW_POLARITY_SHIFT) |
165                            vector;
166
167    ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
168    /* the upper 16 bits are reserved */
169    ioredtbl_state[index] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
170    ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
171}
172