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