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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
/* Copyright 2013-2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include <stdint.h>
#include <ccan/list/list.h>
/*
* Note about interrupt numbers on P7/P7+
* ======================================
*
* The form of an interrupt number in the system on P7/P7+ is as follow:
*
* | Node | T| Chip|GX| BUID | Level |
* |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|
*
* Where:
*
* - Node : The 3-bit node number
* - T : 1 for a Torrent chip, 0 otherwise
* - Chip : 2-bit chip number in a node
* - GX : GX bus identifier
* - BUID : Bus identifier (*)
* - Level : Interrupt number
*
* (*) The BUID/Level distinction is mostly historical, interrupt
* controllers such as the ICS in the PHBs "use" some of the
* low BUID bits as an extension to the interrupt number
*
* The NodeID and ChipID together form a 5-bit Processor Chip ID as
* found in the PIR or in the SPIRA data structures (without the T bit)
*
* PSI interrupt numbering scheme:
* -------------------------------
*
* This is tentatively deduced from stuff I found in some SCOM regs
* and in the BookIV. The PSIHB can be used to specify the 9-bit BUID,
* the Level is always 0. The doc also says that it prepends the 6-bit
* PowerBus chipID (Node + T + Chip). I *assume* that it also prepends
* a 0 in place of the GX bit.
*
* OPAL seems to be arbitrarily using a BUID value of 0x3, I shall do
* the same "just in case" :-)
*
* NOTE: From grep'ing around the giant SCOM file for "Build", I found
* what looks like a register in the GX controller (Mode1
* register) where the PSI BUID can be stored as well. From
* looking around with the FSP getscom command, it appears
* that both pHyp and OPAL set this consistently to the same
* value that appears in the PHB configuration.
*
* => This is confirmed. The NX needs a similar configuration, this
* tells the GX controller not to forward transactions for these
* BUIDs down the GX bus.
*
* PCI interrupt numbering scheme:
* -------------------------------
*
* See IOCs
*
* NX interrupt numbering scheme (p7+):
* ------------------------------------
*
* TBD
*
*
* Additional note about routing of interrupts in P7 and P7+
* =========================================================
*
* There are two on-chip sources of interrupts on these that need a
* special treatment: The PSI interrupt and the NX interrupts.
*
* The problem is that they use the same BUID space as the IO chips
* connected to the GX bus, so the GX controller needs to be told
* about these BUIDs in order to avoid forwarding them down the GX
* link (and possibly choking due to the lack of reply).
*
* The bad news is that it's all undocumented. The good news is that
* I found the info after chatting with Bill Daly (HW design) and
* looking at the SCOM register maps.
*
* The way to set that up differs between P7 and P7+:
*
* - On P7, it's in the GX_MODE1 register at SCOM 0x0201180A, which
* among other things, contains those bits:
*
* 18:26 PSI_BUID: BUID to be used to indicate the interrupt is
* for the PSI
* 27 DISABLE_PSI_BUID: set to 1 to disable the buid reservation
* for PSI
*
* So one must write the 9-bit BUID (without the top chipID) of the
* PSI interrupt in there and clear the disable bit.
*
* - On P7+ it's in the GX_MODE4 register at SCOM 0x02011811
*
* 0 ENABLE_NX_BUID: set to 1 to enable the buid reservation for nx
* 1:9 NX_BUID_BASE: BUID BASE to be used to indicate the interrupt
* is for the nx
* 10:18 NX_BUID_MASK: BUID mask for the nx buid base
* 19:27 PSI_BUID: BUID to be used to indicate the interrupt is for
* the PSI
* 28 DISABLE_PSI_BUID: set to 1 to disable the buid reservation
* for PSI
*
* Note: The NX_BUID_MASK should have bits set to 1 that are relevant for
* the comparison to NX_BUID_BASE, ie 4 interrupts means a mask
* value of b'111111100
*/
#define P7_PSI_IRQ_BUID 0x3 /* 9-bit BUID for the PSI interrupts */
/* Extract individual components of an IRQ number */
#define P7_IRQ_BUID(irq) (((irq) >> 4) & 0x1ff)
#define P7_IRQ_GXID(irq) (((irq) >> 13) & 0x1)
#define P7_IRQ_CHIP(irq) (((irq) >> 14) & 0x3)
#define P7_IRQ_TBIT(irq) (((irq) >> 16) & 0x1)
#define P7_IRQ_NODE(irq) (((irq) >> 17) & 0x7)
/* Extract the "full BUID" (extension + BUID) */
#define P7_IRQ_FBUID(irq) (((irq) >> 4) & 0xffff)
/* BUID Extension (GX + CHIP + T + NODE) */
#define P7_IRQ_BEXT(irq) (((irq) >> 13) & 0x7f)
/* Strip extension from BUID */
#define P7_BUID_BASE(buid) ((buid) & 0x1ff)
/* Note about interrupt numbers on P8
* ==================================
*
* On P8 the interrupts numbers are just a flat space of 19-bit,
* there is no BUID or similar.
*
* However, various unit tend to require blocks of interrupt that
* are naturally power-of-two aligned
*
* Our P8 Interrupt map consits thus of dividing the chip space
* into 4 "blocks" of 2048 interrupts. Block 0 is for random chip
* interrupt sources (NX, PSI, OCC, ...) and keeps sources 0..15
* clear to avoid conflits with IPIs etc.... Block 1..3 are assigned
* to PHB 0..2 respectively.
*
* That gives us an interrupt number made of:
* 18 13 12 11 10 0
* | | | | | |
* +--------------------+------+-----------------------------+
* | Chip# | PHB# | IVE# |
* +--------------------+------+-----------------------------+
*
* We can thus support a max of 2^6 = 64 chips
*
* Each PHB supports 2K interrupt sources, which is shared by
* LSI and MSI. With default configuration, MSI would use range
* [0, 0x7f7] and LSI would use [0x7f8, 0x7ff]. The interrupt
* source should be combined with IRSN to form final hardware
* IRQ.
*
*/
#define P8_CHIP_IRQ_BASE(chip) ((chip) << 13)
#define P8_CHIP_IRQ_BLOCK_BASE(chip, block) (P8_CHIP_IRQ_BASE(chip) \
| ((block) << 11))
#define P8_IRQ_BLOCK_MISC 0
#define P8_IRQ_BLOCK_PHB0 1
#define P8_IRQ_BLOCK_PHB1 2
#define P8_IRQ_BLOCK_PHB2 3
#define P8_CHIP_IRQ_PHB_BASE(chip, phb) (P8_CHIP_IRQ_BLOCK_BASE(chip,\
(phb) + P8_IRQ_BLOCK_PHB0))
#define P8_IRQ_TO_CHIP(irq) (((irq) >> 13) & 0x3f)
#define P8_IRQ_TO_BLOCK(irq) (((irq) >> 11) & 0x03)
#define P8_IRQ_TO_PHB(irq) (P8_IRQ_TO_BLOCK(irq) - \
P8_IRQ_BLOCK_PHB0)
/* Assignment of the "MISC" block:
* -------------------------------
*
* PSI interface has 6 interrupt sources:
*
* FSP, OCC, FSI, LPC, Local error, Host error
*
* and thus needs a block of 8
*/
#define P8_IRQ_MISC_PSI_BASE 0x10 /* 0x10..0x17 */
/* These are handled by skiboot */
#define P8_IRQ_PSI_SKIBOOT_BASE 0
#define P8_IRQ_PSI_FSP 0
#define P8_IRQ_PSI_OCC 1
#define P8_IRQ_PSI_FSI 2
#define P8_IRQ_PSI_LPC 3
#define P8_IRQ_PSI_LOCAL_ERR 4
#define P8_IRQ_PSI_LOCAL_COUNT 5
#define P8_IRQ_PSI_ALL_COUNT 6
/* These are passed onto Linux */
#define P8_IRQ_PSI_LINUX_BASE 5
#define P8_IRQ_PSI_HOST_ERR 5 /* Used for UART */
#define P8_IRQ_PSI_LINUX_COUNT 1
/* TBD: NX, AS, ...
*/
/*
* IRQ sources register themselves here. If an "interrupts" callback
* is provided, then all interrupts in that source will appear in
* 'opal-interrupts' and will be handled by us.
*/
struct irq_source_ops {
int64_t (*set_xive)(void *data, uint32_t isn, uint16_t server,
uint8_t priority);
int64_t (*get_xive)(void *data, uint32_t isn, uint16_t *server,
uint8_t *priority);
void (*interrupt)(void *data, uint32_t isn);
};
extern void register_irq_source(const struct irq_source_ops *ops, void *data,
uint32_t start, uint32_t count);
extern void unregister_irq_source(uint32_t start, uint32_t count);
extern uint32_t get_psi_interrupt(uint32_t chip_id);
extern struct dt_node *add_ics_node(void);
extern void add_opal_interrupts(void);
extern uint32_t get_ics_phandle(void);
struct cpu_thread;
extern void reset_cpu_icp(void);
extern void icp_send_eoi(uint32_t interrupt);
extern void icp_prep_for_rvwinkle(void);
extern void icp_kick_cpu(struct cpu_thread *cpu);
extern void init_interrupts(void);
#endif /* __INTERRUPTS_H */
|