blob: 9239b86ba5f061a92a1279f1fe58901089d0ddba [file] [log] [blame]
/* 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 */