| /* 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 __P7IOC_H |
| #define __P7IOC_H |
| |
| #include <cec.h> |
| #include <pci.h> |
| |
| #include <ccan/container_of/container_of.h> |
| |
| /* |
| * Memory windows and BUID assignment |
| * |
| * - GX BAR assignment |
| * |
| * I don't know of any spec here, so we're going to mimmic what |
| * OPAL seems to be doing: |
| * |
| * - BAR 0 : 32M, disabled. We just leave it alone. |
| * - BAR 1 : 8G, enabled. Appears to correspond to the MMIO |
| * space of the IOC itself and the PCI IO space |
| * - BAR 2: 128G, |
| * - BAR 3: 128G, |
| * - BAR 4: 128G, all 3 contiguous, forming a single 368G region |
| * and is used for M32 and M64 PHB windows. |
| * |
| * - Memory map |
| * |
| * MWIN1 = BAR1 (8G) |
| * MWIN2 = BAR2,3,4 (384G) |
| * |
| * MWIN2 is divided into 6 * 4G regions for use by M32's (*) and |
| * 6 * 32G regions for use by M64's. |
| * |
| * (*) The M32 will typically be configured to only 2G or so, however |
| * the OS is in control of that setting, and since we have to reserve |
| * a power of two, we reserve the whole 4G. |
| * |
| * - RGC registers: MWIN1 + 0x00000000 |
| * - PHBn IO space: MWIN1 + 0x01000000 + n * 0x00800000 (8M each) |
| * - PHBn M32 : MWIN2 + n * 0x1_00000000 (4G each) |
| * - PHBn M64 : MWIN2 + (n + 1) * 0x8_00000000 (32G each) |
| * |
| * - BUID map. The RGC has interrupts, each PHB has then its own |
| * interrupts (errors etc...), 4 LSIs and 256 LSIs so |
| * respectively 1 BUID for self, 1 for LSIs and 16 for LSIs |
| * |
| * We keep all BUIDs below 0x10 reserved. They will be used for things |
| * like the PSI controller, the NX unit, etc.. in the P7 chip. |
| * |
| * RGC : 0x010 |
| * PHBn LSI : 0x040 + n * 0x40 ( 1 BUID) |
| * PHBn MSI : 0x060 + n * 0x40 (0x10 BUIDs) |
| * |
| * -> For routing, each PHB gets a block of 0x40 BUIDs: |
| * |
| * from 0x40 * (n + 1) to 0x7f * (n + 1) |
| */ |
| |
| /* Some definitions resulting from the above description |
| * |
| * Note: A better approach might be to read the GX BAR content |
| * and isolate the biggest contiguous windows. From there |
| * we could divide things algorithmically and thus be |
| * less sensitive to a change in the memory map by the FSP |
| */ |
| #define MWIN1_SIZE 0x200000000ul /* MWIN1 is 8G */ |
| #define MWIN2_SIZE 0x6000000000ul /* MWIN2 is 384G */ |
| #define PHB_IO_OFFSET 0x01000000ul /* Offset of PHB IO space in MWIN1 */ |
| #define PHB_IO_SIZE 0x00800000ul |
| #define PHB_M32_OFFSET 0x0ul /* Offset of PHB M32 space in MWIN2 */ |
| #define PHB_M32_SIZE 0x100000000ul |
| #define PHB_M64_OFFSET 0x800000000ul /* Offset of PHB M64 space in MWIN2 */ |
| #define PHB_M64_SIZE 0x800000000ul |
| #define RGC_BUID_OFFSET 0x10 /* Offset of RGC BUID */ |
| #define PHB_BUID_OFFSET 0x40 /* Offset of PHB BUID blocks */ |
| #define PHB_BUID_SIZE 0x40 /* Size of PHB BUID blocks */ |
| #define PHB_BUID_LSI_OFFSET 0x00 /* Offset of LSI in PHB BUID block */ |
| #define PHB_BUID_MSI_OFFSET 0x20 /* Offset of MSI in PHB BUID block */ |
| #define PHB_BUID_MSI_SIZE 0x10 /* Size of PHB MSI BUID block */ |
| |
| #define PHBn_IO_BASE(n) (PHB_IO_OFFSET + (n) * PHB_IO_SIZE) |
| #define PHBn_M32_BASE(n) (PHB_M32_OFFSET + (n) * PHB_M32_SIZE) |
| #define PHBn_M64_BASE(n) (PHB_M64_OFFSET + (n) * PHB_M64_SIZE) |
| #define PHBn_BUID_BASE(n) (PHB_BUID_OFFSET + (n) * PHB_BUID_SIZE) |
| |
| #define BUID_TO_PHB(buid) (((buid) - PHB_BUID_OFFSET) / PHB_BUID_SIZE) |
| |
| /* p7ioc has 6 PHBs */ |
| #define P7IOC_NUM_PHBS 6 |
| |
| /* M32 window setting at boot: |
| * |
| * To allow for DMA, we need to split the 32-bit PCI address space between |
| * MMIO and DMA. For now, we use a 2G/2G split with MMIO at the top. |
| * |
| * Note: The top 64K of the M32 space are used by MSIs. This is not |
| * visible here but need to be conveyed to the OS one way or another |
| * |
| * Note2: The space reserved in the system address space for M32 is always |
| * 4G. That we chose to use a smaller portion of it is not relevant to |
| * the upper levels. To keep things consistent, the offset we apply to |
| * the window start is also applied on the host side. |
| */ |
| #define M32_PCI_START 0x80000000 |
| #define M32_PCI_SIZE 0x80000000 |
| |
| /* PHB registers exist in both a hard coded space and a programmable |
| * AIB space. We program the latter to the values recommended in the |
| * documentation: |
| * |
| * 0x80000 + n * 0x10000 |
| */ |
| #define PHBn_ASB_BASE(n) (((n) << 16)) |
| #define PHBn_ASB_SIZE 0x10000ul |
| #define PHBn_AIB_BASE(n) (0x80000ul + ((n) << 16)) |
| #define PHBn_AIB_SIZE 0x10000ul |
| |
| /* |
| * LSI interrupts |
| * |
| * The LSI interrupt block supports 8 interrupts. 4 of them are the |
| * standard PCIe INTA..INTB. The rest is for additional functions |
| * of the PHB |
| */ |
| #define PHB_LSI_PCIE_INTA 0 |
| #define PHB_LSI_PCIE_INTB 1 |
| #define PHB_LSI_PCIE_INTC 2 |
| #define PHB_LSI_PCIE_INTD 3 |
| #define PHB_LSI_PCIE_HOTPLUG 4 |
| #define PHB_LSI_PCIE_PERFCTR 5 |
| #define PHB_LSI_PCIE_UNUSED 6 |
| #define PHB_LSI_PCIE_ERROR 7 |
| |
| /* P7IOC PHB slot states */ |
| #define P7IOC_SLOT_NORMAL PCI_SLOT_STATE_NORMAL |
| #define P7IOC_SLOT_LINK PCI_SLOT_STATE_LINK |
| #define P7IOC_SLOT_LINK_START (P7IOC_SLOT_LINK + 1) |
| #define P7IOC_SLOT_LINK_WAIT (P7IOC_SLOT_LINK + 2) |
| #define P7IOC_SLOT_HRESET PCI_SLOT_STATE_HRESET |
| #define P7IOC_SLOT_HRESET_START (P7IOC_SLOT_HRESET + 1) |
| #define P7IOC_SLOT_HRESET_TRAINING (P7IOC_SLOT_HRESET + 2) |
| #define P7IOC_SLOT_HRESET_DELAY (P7IOC_SLOT_HRESET + 3) |
| #define P7IOC_SLOT_HRESET_DELAY2 (P7IOC_SLOT_HRESET + 4) |
| #define P7IOC_SLOT_FRESET PCI_SLOT_STATE_FRESET |
| #define P7IOC_SLOT_FRESET_START (P7IOC_SLOT_FRESET + 1) |
| #define P7IOC_SLOT_FRESET_TRAINING (P7IOC_SLOT_FRESET + 2) |
| #define P7IOC_SLOT_FRESET_POWER_OFF (P7IOC_SLOT_FRESET + 3) |
| #define P7IOC_SLOT_FRESET_POWER_ON (P7IOC_SLOT_FRESET + 4) |
| #define P7IOC_SLOT_FRESET_ASSERT (P7IOC_SLOT_FRESET + 5) |
| #define P7IOC_SLOT_FRESET_DEASSERT (P7IOC_SLOT_FRESET + 6) |
| #define P7IOC_SLOT_CRESET PCI_SLOT_STATE_CRESET |
| #define P7IOC_SLOT_CRESET_START (P7IOC_SLOT_CRESET + 1) |
| |
| /* |
| * In order to support error detection and recovery on different |
| * types of IOCs (e.g. P5IOC, P7IOC, P8IOC), the best bet would |
| * be make the implementation to be 2 layers: OPAL layer and IOC |
| * layer. The OPAL layer just handles the general information and |
| * IOC layer should process much more detailed information, which |
| * is sensitive to itself. |
| */ |
| #define P7IOC_ERR_SRC_NONE 0 |
| #define P7IOC_ERR_SRC_EI 1 |
| #define P7IOC_ERR_SRC_RGC 2 |
| #define P7IOC_ERR_SRC_BI_UP 3 |
| #define P7IOC_ERR_SRC_BI_DOWN 4 |
| #define P7IOC_ERR_SRC_CI_P0 5 |
| #define P7IOC_ERR_SRC_CI_P1 6 |
| #define P7IOC_ERR_SRC_CI_P2 7 |
| #define P7IOC_ERR_SRC_CI_P3 8 |
| #define P7IOC_ERR_SRC_CI_P4 9 |
| #define P7IOC_ERR_SRC_CI_P5 10 |
| #define P7IOC_ERR_SRC_CI_P6 11 |
| #define P7IOC_ERR_SRC_CI_P7 12 |
| #define P7IOC_ERR_SRC_PHB0 13 |
| #define P7IOC_ERR_SRC_PHB1 14 |
| #define P7IOC_ERR_SRC_PHB2 15 |
| #define P7IOC_ERR_SRC_PHB3 16 |
| #define P7IOC_ERR_SRC_PHB4 17 |
| #define P7IOC_ERR_SRC_PHB5 18 |
| #define P7IOC_ERR_SRC_MISC 19 |
| #define P7IOC_ERR_SRC_I2C 20 |
| #define P7IOC_ERR_SRC_LAST 21 |
| |
| #define P7IOC_ERR_CLASS_NONE 0 |
| #define P7IOC_ERR_CLASS_GXE 1 |
| #define P7IOC_ERR_CLASS_PLL 2 |
| #define P7IOC_ERR_CLASS_RGA 3 |
| #define P7IOC_ERR_CLASS_PHB 4 |
| #define P7IOC_ERR_CLASS_ER 5 |
| #define P7IOC_ERR_CLASS_INF 6 |
| #define P7IOC_ERR_CLASS_MAL 7 |
| #define P7IOC_ERR_CLASS_LAST 8 |
| |
| /* |
| * P7IOC error descriptor. For errors from PHB and PE, they |
| * will be cached to the corresponding PHBs. However, the |
| * left errors (e.g. EI, CI Port0/1) will be cached to the |
| * IOC directly. |
| */ |
| struct p7ioc_err { |
| uint32_t err_src; |
| uint32_t err_class; |
| uint32_t err_bit; |
| }; |
| |
| struct p7ioc; |
| |
| #define P7IOC_PHB_CFG_USE_ASB 0x00000001 /* ASB to access PCI-CFG */ |
| #define P7IOC_PHB_CFG_BLOCKED 0x00000002 /* PCI-CFG blocked except 0 */ |
| |
| struct p7ioc_phb { |
| uint8_t index; /* 0..5 index inside p7ioc */ |
| uint8_t gen; |
| uint32_t flags; |
| bool broken; |
| #define P7IOC_REV_DD10 0x00a20001 |
| #define P7IOC_REV_DD11 0x00a20002 |
| uint32_t rev; /* Both major and minor have 2 bytes */ |
| void *regs_asb; |
| void *regs; /* AIB regs */ |
| uint32_t buid_lsi; |
| uint32_t buid_msi; |
| uint64_t io_base; |
| uint64_t m32_base; |
| uint64_t m64_base; |
| int64_t ecap; /* cached PCI-E cap offset */ |
| int64_t aercap; /* cached AER ecap offset */ |
| uint64_t lxive_cache[8]; |
| uint64_t mxive_cache[256]; |
| uint64_t mve_cache[256]; |
| uint64_t peltm_cache[128]; |
| uint64_t peltv_lo_cache[128]; |
| uint64_t peltv_hi_cache[128]; |
| uint64_t tve_lo_cache[128]; |
| uint64_t tve_hi_cache[128]; |
| uint64_t iod_cache[128]; |
| uint64_t m32d_cache[128]; |
| uint64_t m64b_cache[16]; |
| uint64_t m64d_cache[128]; |
| bool err_pending; |
| struct p7ioc_err err; |
| struct p7ioc *ioc; |
| struct phb phb; |
| }; |
| |
| static inline struct p7ioc_phb *phb_to_p7ioc_phb(struct phb *phb) |
| { |
| return container_of(phb, struct p7ioc_phb, phb); |
| } |
| |
| static inline bool p7ioc_phb_err_pending(struct p7ioc_phb *p) |
| { |
| return p->err_pending; |
| } |
| |
| static inline void p7ioc_phb_set_err_pending(struct p7ioc_phb *p, bool pending) |
| { |
| if (!pending) { |
| p->err.err_src = P7IOC_ERR_SRC_NONE; |
| p->err.err_class = P7IOC_ERR_CLASS_NONE; |
| p->err.err_bit = -1; |
| } |
| |
| p->err_pending = pending; |
| } |
| |
| /* |
| * State structure for P7IOC IO HUB |
| */ |
| struct p7ioc { |
| /* Device node */ |
| struct dt_node *dt_node; |
| |
| /* MMIO regs */ |
| void *regs; |
| |
| /* Main MMIO window from GX for registers & PCI IO space */ |
| uint64_t mmio1_win_start; |
| uint64_t mmio1_win_size; |
| |
| /* Secondary MMIO window for PCI MMIO space */ |
| uint64_t mmio2_win_start; |
| uint64_t mmio2_win_size; |
| |
| /* BUID base for the PHB. This does include the top bits |
| * (chip, GX bus ID, etc...). This is initialized from the |
| * SPIRA. It does not contain the offset 0x10 for RGC |
| * interrupts. |
| * |
| * The OPAL-defined "interrupt-base" property will contain |
| * the RGC BUID, not this base value, since this is the real |
| * starting point of interrupts for the IOC and we don't want |
| * to cover the BUID 0..f gap which is reserved for P7 on-chip |
| * interrupt sources. |
| */ |
| uint32_t buid_base; |
| uint32_t rgc_buid; |
| |
| /* XIVT cache for RGC interrupts */ |
| uint64_t xive_cache[16]; |
| bool err_pending; |
| struct p7ioc_err err; |
| |
| /* PHB array & presence detect */ |
| struct p7ioc_phb phbs[P7IOC_NUM_PHBS]; |
| uint8_t phb_pdt; |
| |
| struct io_hub hub; |
| }; |
| |
| static inline struct p7ioc *iohub_to_p7ioc(struct io_hub *hub) |
| { |
| return container_of(hub, struct p7ioc, hub); |
| } |
| |
| static inline bool p7ioc_err_pending(struct p7ioc *ioc) |
| { |
| return ioc->err_pending; |
| } |
| |
| static inline void p7ioc_set_err_pending(struct p7ioc *ioc, bool pending) |
| { |
| if (!pending) { |
| ioc->err.err_src = P7IOC_ERR_SRC_NONE; |
| ioc->err.err_class = P7IOC_ERR_CLASS_NONE; |
| ioc->err.err_bit = -1; |
| } |
| |
| ioc->err_pending = pending; |
| } |
| |
| static inline bool p7ioc_phb_enabled(struct p7ioc *ioc, unsigned int phb) |
| { |
| return !!(ioc->phb_pdt & (0x80 >> phb)); |
| } |
| |
| extern int64_t p7ioc_inits(struct p7ioc *ioc); |
| |
| extern void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index); |
| extern int64_t p7ioc_phb_init(struct p7ioc_phb *p); |
| |
| extern bool p7ioc_check_LEM(struct p7ioc *ioc, uint16_t *pci_error_type, |
| uint16_t *severity); |
| extern int64_t p7ioc_phb_get_xive(struct p7ioc_phb *p, uint32_t isn, |
| uint16_t *server, uint8_t *prio); |
| extern int64_t p7ioc_phb_set_xive(struct p7ioc_phb *p, uint32_t isn, |
| uint16_t server, uint8_t prio); |
| extern void p7ioc_reset(struct io_hub *hub); |
| extern void p7ioc_phb_reset(struct phb *phb); |
| |
| #endif /* __P7IOC_H */ |