summaryrefslogtreecommitdiffstats
path: root/include/interrupts.h
blob: 2c4fa7e92399f39856ff049d004c92e794ad8160 (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
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/* 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 "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..n are assigned
 * to PHB 0..n respectively. The number of blocks is determined by the
 * number of bits assigned to chips.
 *
 * That gives us an interrupt number made of:
 *  18               n+1 n   11  10                         0
 *  |                  | |    | |                           |
 * +--------------------+------+-----------------------------+
 * |        Chip#       | PHB# |             IVE#            |
 * +--------------------+------+-----------------------------+
 *
 * Where n = 18 - p8_chip_id_bits
 *
 * For P8 we have 6 bits for Chip# as defined by p8_chip_id_bits. We
 * therefore support a max of 2^6 = 64 chips.
 *
 * For P8NVL we have an extra PHB and so we assign 5 bits for Chip#
 * and therefore support a max of 32 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.
 *
 */

uint32_t p8_chip_irq_block_base(uint32_t chip, uint32_t block);
uint32_t p8_chip_irq_phb_base(uint32_t chip, uint32_t phb);
uint32_t p8_irq_to_chip(uint32_t irq);
uint32_t p8_irq_to_block(uint32_t irq);
uint32_t p8_irq_to_phb(uint32_t irq);

/* Total number of bits in the P8 interrupt space */
#define P8_IRQ_BITS		19

/* Number of bits per block */
#define P8_IVE_BITS		11

#define P8_IRQ_BLOCK_MISC	0
#define P8_IRQ_BLOCK_PHB_BASE	1

/* 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_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_EXTERNAL		5	/* Used for UART */
#define P8_IRQ_PSI_IRQ_COUNT		6

/* TBD: NX, AS, ...
 */

/* Note about interrupt numbers on P9
 * ==================================
 *
 * P9 uses a completely different interrupt controller, XIVE.
 *
 * It targets objects using a combination of block number and
 * index within a block. However, we try to avoid exposing that
 * split to the OS in order to keep some abstraction in case the
 * way we allocate these change.
 *
 * The lowest level entity in Xive is the ESB (state bits).
 *
 * Those are spread between PHBs, PSI bridge and XIVE itself which
 * provide a large amount of state bits for IPIs and other SW and HW
 * generated interrupts by sources that don't have their own ESB logic
 *
 * Due to that spread, they aren't a good representation of a global
 * interrupt number.
 *
 * Each such source however needs to be targetted at an EAS (IVT)
 * entry in a table which will control targetting information and
 * associate that interrupt with a logical number.
 *
 * Thus that table entry number represents a good "global interrupt
 * number". Additionally, for the host OS, we will keep the logical
 * number equal to the global number.
 *
 * The details of how these are assigned on P9 can be found in
 * hw/xive.c. P9 HW will only use a subset of the definitions and
 * functions in this file (or the corresponding core/interrupts.c).
 */

struct irq_source;

/*
 * IRQ sources register themselves here.
 *
 * The "attributes" callback provides various attributes specific to
 * a given interrupt, such as whether it's targetted at OPAL or the
 * OS, or whether it's frequent or infrequent. The latter will be used
 * later to optimize the lookup of the sources array by providing a small
 * cache of the frequent interrupts.
 *
 * The "eoi" callback is used for XIVE interrupts in XICS emulation
 * though we might expose it at some point in XIVE native mode for
 * interrupts that require special EOI operations such as possibly
 * the LPC interrupts on P9 that need a latch cleared in the LPCHC.
 *
 * The "name" callback returns a name for the interrupt in a new
 * malloc()'ed block. The caller will free() it. NULL is acceptable.
 */
struct irq_source_ops {
	int64_t (*set_xive)(struct irq_source *is, uint32_t isn,
			    uint16_t server, uint8_t priority);
	int64_t (*get_xive)(struct irq_source *is, uint32_t isn,
			    uint16_t *server, uint8_t *priority);
	uint64_t (*attributes)(struct irq_source *is, uint32_t isn);
/* LSB is the target */
#define IRQ_ATTR_TARGET_OPAL		0x0
#define IRQ_ATTR_TARGET_LINUX		0x1
/* For OPAL interrupts, estimate frequency */
#define IRQ_ATTR_TARGET_RARE		0x0
#define IRQ_ATTR_TARGET_FREQUENT	0x2
/* For OPAL interrupts, level vs. edge setting */
#define IRQ_ATTR_TYPE_LSI		0x0
#define IRQ_ATTR_TYPE_MSI		0x4
	void (*interrupt)(struct irq_source *is, uint32_t isn);
	void (*eoi)(struct irq_source *is, uint32_t isn);
	char *(*name)(struct irq_source *is, uint32_t isn);
};

struct irq_source {
	uint32_t			start;
	uint32_t			end;
	const struct irq_source_ops	*ops;
	void				*data;
	struct list_node		link;
};

extern void __register_irq_source(struct irq_source *is, bool secondary);
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 struct irq_source *irq_find_source(uint32_t isn);

/* Warning: callback is called with internal source lock held
 * so don't call back into any of our irq_ APIs from it
 */
extern void irq_for_each_source(void (*cb)(struct irq_source *, void *),
				void *data);

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 reset_cpu_xive(void);
extern void icp_send_eoi(uint32_t interrupt);
extern void icp_prep_for_pm(void);
extern void icp_kick_cpu(struct cpu_thread *cpu);

extern void init_interrupts(void);

extern bool irq_source_eoi(uint32_t isn);
extern bool __irq_source_eoi(struct irq_source *is, uint32_t isn);


#endif /* __INTERRUPTS_H */
OpenPOWER on IntegriCloud