/* 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. */ /* * Service Processor serial console handling code */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static LIST_HEAD(psis); static u64 psi_link_timer; static u64 psi_link_timeout; static bool psi_link_poll_active; static bool psi_ext_irq_policy = EXTERNAL_IRQ_POLICY_LINUX; static void psi_activate_phb(struct psi *psi); static struct lock psi_lock = LOCK_UNLOCKED; DEFINE_LOG_ENTRY(OPAL_RC_PSI_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_PSI, OPAL_PLATFORM_FIRMWARE, OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION, OPAL_NA); void psi_set_link_polling(bool active) { printf("PSI: %sing link polling\n", active ? "start" : "stopp"); psi_link_poll_active = active; } void psi_disable_link(struct psi *psi) { lock(&psi_lock); /* * Note: This can be called with the link already down but * not detected as such yet by this layer since psi_check_link_active() * operates locklessly and thus won't update the PSI structure. This * is a non-issue, the only consequence is the messages in the log * mentioning first the link having gone down then being disabled. */ if (psi->active) { u64 reg; psi->active = false; /* Mask errors in SEMR */ reg = in_be64(psi->regs + PSIHB_SEMR); reg = ((0xfffull << 36) | (0xfffull << 20)); out_be64(psi->regs + PSIHB_SEMR, reg); printf("PSI: SEMR set to %llx\n", reg); /* Reset all the error bits in PSIHB_CR and * disable FSP interrupts */ reg = in_be64(psi->regs + PSIHB_CR); reg &= ~(0x7ffull << 20); reg &= ~PSIHB_CR_PSI_LINK_ENABLE; /* flip link enable */ /* * Ensure no commands/spurious interrupts reach * the processor, by flipping the command enable. */ reg &= ~PSIHB_CR_FSP_CMD_ENABLE; reg &= ~PSIHB_CR_FSP_IRQ_ENABLE; reg &= ~PSIHB_CR_FSP_IRQ; /* Clear interrupt state too */ printf("PSI[0x%03x]: Disabling link!\n", psi->chip_id); out_be64(psi->regs + PSIHB_CR, reg); printf("PSI: PSIHB_CR (error bits) set to %llx\n", in_be64(psi->regs + PSIHB_CR)); psi_set_link_polling(true); } unlock(&psi_lock); } /* * Resetting the FSP is a multi step sequence: * 1. Read the PSIHBCR * 2. Set the PSIHBCR[6] -- write register back. * 3. Read PSIHBCR again * 4. Reset PSIHBCR[6] -- write register back. */ void psi_reset_fsp(struct psi *psi) { lock(&psi_lock); if (psi->active) { u64 reg; printf("PSI: Driving FSP reset via PSI\n"); reg = in_be64(psi->regs + PSIHB_CR); reg &= ~(0xfffull << 20); /* Reset error bits */ reg |= PSIHB_CR_FSP_RESET; /* FSP reset trigger start */ out_be64(psi->regs + PSIHB_CR, reg); printf("PSI[0x%03x]: FSP reset start PSIHBCR set to %llx\n", psi->chip_id, in_be64(psi->regs + PSIHB_CR)); reg = in_be64(psi->regs + PSIHB_CR); reg &= ~PSIHB_CR_FSP_RESET; /* Clear FSP reset bit */ out_be64(psi->regs + PSIHB_CR, reg); /* Complete reset */ printf("PSI[0x%03x]: FSP reset complete. PSIHBCR set to %llx\n", psi->chip_id, in_be64(psi->regs + PSIHB_CR)); } unlock(&psi_lock); /* Now bring down the PSI link too... */ psi_disable_link(psi); } bool psi_check_link_active(struct psi *psi) { u64 val = in_be64(psi->regs + PSIHB_CR); /* * Unlocked, used during fsp_poke_msg so we really want * to avoid fancy link re-entrancy and deadlocks here */ if (!psi->active) return false; return (val & PSIHB_CR_PSI_LINK_ENABLE) && (val & PSIHB_CR_FSP_LINK_ACTIVE); } struct psi *psi_find_link(uint32_t chip_id) { struct psi *psi; list_for_each(&psis, psi, list) { if (psi->chip_id == chip_id) return psi; } return NULL; } #define PSI_LINK_CHECK_INTERVAL 10 /* Interval in secs */ #define PSI_LINK_RECOVERY_TIMEOUT 1800 /* 30 minutes */ static void psi_link_poll(void *data __unused) { struct psi *psi; u64 now; if (!psi_link_poll_active) return; now = mftb(); if (psi_link_timer == 0 || (tb_compare(now, psi_link_timer) == TB_AAFTERB) || (tb_compare(now, psi_link_timer) == TB_AEQUALB)) { lock(&psi_lock); list_for_each(&psis, psi, list) { u64 val; if (psi->active) continue; val = in_be64(psi->regs + PSIHB_CR); printf("PSI[0x%03x]: Poll CR=0x%016llx\n", psi->chip_id, val); if ((val & PSIHB_CR_PSI_LINK_ENABLE) && (val & PSIHB_CR_FSP_LINK_ACTIVE)) { printf("PSI[0x%03x]: Found active link!\n", psi->chip_id); psi_link_timeout = 0; psi->active = true; psi_activate_phb(psi); psi_set_link_polling(false); unlock(&psi_lock); fsp_reinit_fsp(); return; } } if (!psi_link_timeout) psi_link_timeout = now + secs_to_tb(PSI_LINK_RECOVERY_TIMEOUT); if (tb_compare(now, psi_link_timeout) == TB_AAFTERB) { log_simple_error(&e_info(OPAL_RC_PSI_TIMEOUT), "PSI: Link timeout -- loss of FSP\n"); /* Reset the link timeout and continue looking */ psi_link_timeout = 0; } /* Poll every 10 seconds */ psi_link_timer = now + secs_to_tb(PSI_LINK_CHECK_INTERVAL); unlock(&psi_lock); } } void psi_enable_fsp_interrupt(struct psi *psi) { /* Enable FSP interrupts in the GXHB */ lock(&psi_lock); out_be64(psi->regs + PSIHB_CR, in_be64(psi->regs + PSIHB_CR) | PSIHB_CR_FSP_IRQ_ENABLE); unlock(&psi_lock); } /* Multiple bits can be set on errors */ static void decode_psihb_error(u64 val) { if (val & PSIHB_CR_PSI_ERROR) printf("PSI: PSI Reported Error\n"); if (val & PSIHB_CR_PSI_LINK_INACTIVE) printf("PSI: PSI Link Inactive Transition\n"); if (val & PSIHB_CR_FSP_ACK_TIMEOUT) printf("PSI: FSP Ack Timeout\n"); if (val & PSIHB_CR_MMIO_LOAD_TIMEOUT) printf("PSI: MMIO Load Timeout\n"); if (val & PSIHB_CR_MMIO_LENGTH_ERROR) printf("PSI: MMIO Length Error\n"); if (val & PSIHB_CR_MMIO_ADDRESS_ERROR) printf("PSI: MMIO Address Error\n"); if (val & PSIHB_CR_MMIO_TYPE_ERROR) printf("PSI: MMIO Type Error\n"); if (val & PSIHB_CR_UE) printf("PSI: UE Detected\n"); if (val & PSIHB_CR_PARITY_ERROR) printf("PSI: Internal Parity Error\n"); if (val & PSIHB_CR_SYNC_ERR_ALERT1) printf("PSI: Sync Error Alert1\n"); if (val & PSIHB_CR_SYNC_ERR_ALERT2) printf("PSI: Sync Error Alert2\n"); if (val & PSIHB_CR_FSP_COMMAND_ERROR) printf("PSI: FSP Command Error\n"); } static void handle_psi_interrupt(struct psi *psi, u64 val) { printf("PSI[0x%03x]: PSI mgmnt interrupt CR=0x%016llx\n", psi->chip_id, val); if (val & (0xfffull << 20)) { decode_psihb_error(val); psi_disable_link(psi); } else if (val & (0x1full << 11)) printf("PSI: FSP error detected\n"); } static void psi_spurious_fsp_irq(struct psi *psi) { u64 reg, bit; prerror("PSI: Spurious interrupt, attempting clear\n"); if (proc_gen == proc_gen_p9) { reg = PSIHB_XSCOM_P9_HBCSR_CLR; bit = PSIHB_XSCOM_P9_HBSCR_FSP_IRQ; } else if (proc_gen == proc_gen_p8) { reg = PSIHB_XSCOM_P8_HBCSR_CLR; bit = PSIHB_XSCOM_P8_HBSCR_FSP_IRQ; } else { reg = PSIHB_XSCOM_P7_HBCSR_CLR; bit = PSIHB_XSCOM_P7_HBSCR_FSP_IRQ; } xscom_write(psi->chip_id, psi->xscom_base + reg, bit); } bool psi_poll_fsp_interrupt(struct psi *psi) { return !!(in_be64(psi->regs + PSIHB_CR) & PSIHB_CR_FSP_IRQ); } static void psihb_interrupt(struct irq_source *is, uint32_t isn __unused) { struct psi *psi = is->data; u64 val; val = in_be64(psi->regs + PSIHB_CR); if (psi_link_poll_active) { printf("PSI[0x%03x]: PSI interrupt CR=0x%016llx (A=%d)\n", psi->chip_id, val, psi->active); } /* Handle PSI interrupts first in case it's a link down */ if (val & PSIHB_CR_PSI_IRQ) { handle_psi_interrupt(psi, val); /* * If the link went down, re-read PSIHB_CR as * the FSP interrupt might have been cleared. */ if (!psi->active) val = in_be64(psi->regs + PSIHB_CR); } /* * We avoid forwarding FSP interrupts if the link isn't * active. They should be masked anyway but it looks * like the CR bit can remain set. */ if (val & PSIHB_CR_FSP_IRQ) { /* * We have a case a flood with FSP mailbox interrupts * when the link is down, see if we manage to clear * the condition */ if (!psi->active) psi_spurious_fsp_irq(psi); else fsp_interrupt(); } /* Poll the console buffers on any interrupt since we don't * get send notifications */ fsp_console_poll(NULL); } static int64_t psi_p7_set_xive(struct irq_source *is, uint32_t isn __unused, uint16_t server, uint8_t priority) { struct psi *psi = is->data; uint64_t xivr; /* Populate the XIVR */ xivr = (uint64_t)server << 40; xivr |= (uint64_t)priority << 32; xivr |= P7_IRQ_BUID(psi->interrupt) << 16; out_be64(psi->regs + PSIHB_XIVR, xivr); return OPAL_SUCCESS; } static int64_t psi_p7_get_xive(struct irq_source *is, uint32_t isn __unused, uint16_t *server, uint8_t *priority) { struct psi *psi = is->data; uint64_t xivr; /* Read & decode the XIVR */ xivr = in_be64(psi->regs + PSIHB_XIVR); *server = (xivr >> 40) & 0x7ff; *priority = (xivr >> 32) & 0xff; return OPAL_SUCCESS; } static uint64_t psi_p7_irq_attributes(struct irq_source *is __unused, uint32_t isn __unused) { return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TARGET_FREQUENT; } static const uint32_t psi_p8_irq_to_xivr[P8_IRQ_PSI_IRQ_COUNT] = { [P8_IRQ_PSI_FSP] = PSIHB_XIVR_FSP, [P8_IRQ_PSI_OCC] = PSIHB_XIVR_OCC, [P8_IRQ_PSI_FSI] = PSIHB_XIVR_FSI, [P8_IRQ_PSI_LPC] = PSIHB_XIVR_LPC, [P8_IRQ_PSI_LOCAL_ERR] = PSIHB_XIVR_LOCAL_ERR, [P8_IRQ_PSI_EXTERNAL]= PSIHB_XIVR_HOST_ERR, }; static void psi_cleanup_irq(struct psi *psi) { uint32_t irq; uint64_t xivr, xivr_p; for (irq = 0; irq < P8_IRQ_PSI_IRQ_COUNT; irq++) { prlog(PR_DEBUG, "PSI[0x%03x]: Cleaning up IRQ %d\n", psi->chip_id, irq); xivr_p = psi_p8_irq_to_xivr[irq]; xivr = in_be64(psi->regs + xivr_p); xivr |= (0xffull << 32); out_be64(psi->regs + xivr_p, xivr); time_wait_ms_nopoll(10); xivr = in_be64(psi->regs + xivr_p); if (xivr & PPC_BIT(39)) { printf(" Need EOI !\n"); icp_send_eoi(psi->interrupt + irq); } } } /* Called on a fast reset, make sure we aren't stuck with * an accepted and never EOId PSI interrupt */ void psi_irq_reset(void) { struct psi *psi; printf("PSI: Hot reset!\n"); assert(proc_gen == proc_gen_p8); list_for_each(&psis, psi, list) { psi_cleanup_irq(psi); } } static const struct irq_source_ops psi_p7_irq_ops = { .get_xive = psi_p7_get_xive, .set_xive = psi_p7_set_xive, .interrupt = psihb_interrupt, .attributes = psi_p7_irq_attributes, }; static int64_t psi_p8_set_xive(struct irq_source *is, uint32_t isn, uint16_t server, uint8_t priority) { struct psi *psi = is->data; uint64_t xivr_p, xivr; uint32_t irq_idx = isn & 7; if (irq_idx >= P8_IRQ_PSI_IRQ_COUNT) return OPAL_PARAMETER; xivr_p = psi_p8_irq_to_xivr[irq_idx]; /* Populate the XIVR */ xivr = (uint64_t)server << 40; xivr |= (uint64_t)priority << 32; xivr |= (uint64_t)(isn & 7) << 29; out_be64(psi->regs + xivr_p, xivr); return OPAL_SUCCESS; } static int64_t psi_p8_get_xive(struct irq_source *is, uint32_t isn __unused, uint16_t *server, uint8_t *priority) { struct psi *psi = is->data; uint64_t xivr_p, xivr; uint32_t irq_idx = isn & 7; if (irq_idx >= P8_IRQ_PSI_IRQ_COUNT) return OPAL_PARAMETER; xivr_p = psi_p8_irq_to_xivr[irq_idx]; /* Read & decode the XIVR */ xivr = in_be64(psi->regs + xivr_p); *server = (xivr >> 40) & 0xffff; *priority = (xivr >> 32) & 0xff; return OPAL_SUCCESS; } static void psihb_p8_interrupt(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; uint32_t idx = isn - psi->interrupt; switch (idx) { case P8_IRQ_PSI_FSP: psihb_interrupt(is, isn); break; case P8_IRQ_PSI_OCC: occ_interrupt(psi->chip_id); break; case P8_IRQ_PSI_FSI: printf("PSI: FSI irq received\n"); break; case P8_IRQ_PSI_LPC: lpc_interrupt(psi->chip_id); /* * i2c interrupts are ORed with the LPC ones on * Murano DD2.1 and Venice DD2.0 */ p8_i2c_interrupt(psi->chip_id); break; case P8_IRQ_PSI_LOCAL_ERR: prd_psi_interrupt(psi->chip_id); break; case P8_IRQ_PSI_EXTERNAL: if (platform.external_irq) platform.external_irq(psi->chip_id); break; } /* * TODO: Per Vicente Chung, CRESPs don't generate interrupts, * and are just informational. Need to define the policy * to handle them. */ } static uint64_t psi_p8_irq_attributes(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; uint32_t idx = isn - psi->interrupt; uint64_t attr; if (psi->no_lpc_irqs && idx == P8_IRQ_PSI_LPC) return IRQ_ATTR_TARGET_LINUX; if (idx == P8_IRQ_PSI_EXTERNAL && psi_ext_irq_policy == EXTERNAL_IRQ_POLICY_LINUX) return IRQ_ATTR_TARGET_LINUX; attr = IRQ_ATTR_TARGET_OPAL; if (idx == P8_IRQ_PSI_EXTERNAL || idx == P8_IRQ_PSI_LPC || idx == P8_IRQ_PSI_FSP) attr |= IRQ_ATTR_TARGET_FREQUENT; return attr; } static char *psi_p8_irq_name(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; uint32_t idx = isn - psi->interrupt; static const char *names[P8_IRQ_PSI_IRQ_COUNT] = { "psi:fsp", "psi:occ", "psi:fsi", "psi:lpchc", "psi:local_err", "psi:external", }; if (idx >= P8_IRQ_PSI_IRQ_COUNT) return NULL; return strdup(names[idx]); } static const struct irq_source_ops psi_p8_irq_ops = { .get_xive = psi_p8_get_xive, .set_xive = psi_p8_set_xive, .interrupt = psihb_p8_interrupt, .attributes = psi_p8_irq_attributes, .name = psi_p8_irq_name, }; static void psihb_p9_interrupt(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; uint32_t idx = isn - psi->interrupt; switch (idx) { case P9_PSI_IRQ_PSI: psihb_interrupt(is, isn); break; case P9_PSI_IRQ_OCC: occ_interrupt(psi->chip_id); break; case P9_PSI_IRQ_FSI: printf("PSI: FSI irq received\n"); break; case P9_PSI_IRQ_LPCHC: lpc_interrupt(psi->chip_id); break; case P9_PSI_IRQ_LOCAL_ERR: prd_psi_interrupt(psi->chip_id); break; case P9_PSI_IRQ_GLOBAL_ERR: printf("PSI: Global error irq received\n"); break; case P9_PSI_IRQ_EXTERNAL: if (platform.external_irq) platform.external_irq(psi->chip_id); break; case P9_PSI_IRQ_LPC_SIRQ0: case P9_PSI_IRQ_LPC_SIRQ1: case P9_PSI_IRQ_LPC_SIRQ2: case P9_PSI_IRQ_LPC_SIRQ3: lpc_serirq(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0); break; case P9_PSI_IRQ_SBE_I2C: p8_i2c_interrupt(psi->chip_id); break; case P9_PSI_IRQ_DIO: printf("PSI: DIO irq received\n"); break; case P9_PSI_IRQ_PSU: printf("PSI: PSU irq received\n"); break; } } static uint64_t psi_p9_irq_attributes(struct irq_source *is __unused, uint32_t isn) { struct psi *psi = is->data; unsigned int idx = isn & 0xf; /* If LPC interrupts are disabled, route them to Linux * (who will not request them since they aren't referenced * in the device tree) */ if (psi->no_lpc_irqs && (idx == P9_PSI_IRQ_LPC_SIRQ0 || idx == P9_PSI_IRQ_LPC_SIRQ1 || idx == P9_PSI_IRQ_LPC_SIRQ2 || idx == P9_PSI_IRQ_LPC_SIRQ3 || idx == P9_PSI_IRQ_LPCHC)) return IRQ_ATTR_TARGET_LINUX; /* XXX For now, all go to OPAL, this will change */ return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TARGET_FREQUENT; } static char *psi_p9_irq_name(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; uint32_t idx = isn - psi->interrupt; static const char *names[P9_PSI_NUM_IRQS] = { "psi:fsp", "psi:occ", "psi:fsi", "psi:lpchc", "psi:local_err", "psi:global_err", "psi:external", "psi:lpc_serirq_mux0", /* Have a callback to get name ? */ "psi:lpc_serirq_mux1", /* Have a callback to get name ? */ "psi:lpc_serirq_mux2", /* Have a callback to get name ? */ "psi:lpc_serirq_mux3", /* Have a callback to get name ? */ "psi:i2c", "psi:dio", "psi:psu" }; if (idx >= P9_PSI_NUM_IRQS) return NULL; return strdup(names[idx]); } static void psi_p9_irq_dd1_eoi(struct irq_source *is, uint32_t isn) { struct psi *psi = is->data; unsigned int idx = isn & 0xf; if (idx >= P9_PSI_IRQ_LPC_SIRQ0 && idx <= P9_PSI_IRQ_LPC_SIRQ3) lpc_p9_sirq_eoi(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0); __xive_source_eoi(is, isn); } static const struct irq_source_ops psi_p9_dd1_irq_ops = { .interrupt = psihb_p9_interrupt, .attributes = psi_p9_irq_attributes, .name = psi_p9_irq_name, .eoi = psi_p9_irq_dd1_eoi, }; static const struct irq_source_ops psi_p9_irq_ops = { .interrupt = psihb_p9_interrupt, .attributes = psi_p9_irq_attributes, .name = psi_p9_irq_name, }; static void psi_tce_enable(struct psi *psi, bool enable) { void *addr; u64 val; switch (proc_gen) { case proc_gen_p7: addr = psi->regs + PSIHB_CR; break; case proc_gen_p8: case proc_gen_p9: addr = psi->regs + PSIHB_PHBSCR; break; default: prerror("%s: Unknown CPU type\n", __func__); return; } val = in_be64(addr); if (enable) val |= PSIHB_CR_TCE_ENABLE; else val &= ~PSIHB_CR_TCE_ENABLE; out_be64(addr, val); } /* * Configure the PSI interface for communicating with * an FSP, such as enabling the TCEs, FSP commands, * etc... */ void psi_init_for_fsp(struct psi *psi) { uint64_t reg; bool enable_tce = true; lock(&psi_lock); /* Disable and setup TCE base address */ psi_tce_enable(psi, false); switch (proc_gen) { case proc_gen_p7: out_be64(psi->regs + PSIHB_TAR, PSI_TCE_TABLE_BASE | PSIHB_TAR_16K_ENTRIES); break; case proc_gen_p8: case proc_gen_p9: out_be64(psi->regs + PSIHB_TAR, PSI_TCE_TABLE_BASE | PSIHB_TAR_256K_ENTRIES); break; default: enable_tce = false; }; /* Enable various other configuration register bits based * on what pHyp does. We keep interrupts disabled until * after the mailbox has been properly configured. We assume * basic stuff such as PSI link enable is already there. * * - FSP CMD Enable * - FSP MMIO Enable * - TCE Enable * - Error response enable * * Clear all other error bits */ if (!psi->active) { prerror("PSI: psi_init_for_fsp() called on inactive link!\n"); unlock(&psi_lock); return; } reg = in_be64(psi->regs + PSIHB_CR); reg |= PSIHB_CR_FSP_CMD_ENABLE; reg |= PSIHB_CR_FSP_MMIO_ENABLE; reg |= PSIHB_CR_FSP_ERR_RSP_ENABLE; reg &= ~0x00000000ffffffffull; out_be64(psi->regs + PSIHB_CR, reg); psi_tce_enable(psi, enable_tce); unlock(&psi_lock); } void psi_set_external_irq_policy(bool policy) { psi_ext_irq_policy = policy; } static void psi_init_p7_interrupt(struct psi *psi) { /* On P7, we get a single interrupt */ out_be64(psi->regs + PSIHB_XIVR, P7_IRQ_BUID(psi->interrupt) << 16 | 0xffull << 32); /* Configure it in the GX controller as well */ gx_configure_psi_buid(psi->chip_id, P7_IRQ_BUID(psi->interrupt)); /* Register the IRQ source */ register_irq_source(&psi_p7_irq_ops, psi, psi->interrupt, 1); } static void psi_init_p8_interrupts(struct psi *psi) { uint32_t irq; uint64_t xivr_p; /* On P8 we get a block of 8, set up the base/mask * and mask all the sources for now */ out_be64(psi->regs + PSIHB_IRSN, SETFIELD(PSIHB_IRSN_COMP, 0ul, psi->interrupt) | SETFIELD(PSIHB_IRSN_MASK, 0ul, 0x7fff8ul) | PSIHB_IRSN_DOWNSTREAM_EN | PSIHB_IRSN_UPSTREAM_EN); for (irq = 0; irq < P8_IRQ_PSI_IRQ_COUNT; irq++) { xivr_p = psi_p8_irq_to_xivr[irq]; out_be64(psi->regs + xivr_p, (0xffull << 32) | (irq << 29)); } /* * Register the IRQ sources FSP, OCC, FSI, LPC * and Local Error. Host Error is actually the * external interrupt and the policy for that comes * from the platform */ register_irq_source(&psi_p8_irq_ops, psi, psi->interrupt, P8_IRQ_PSI_IRQ_COUNT); } static void psi_init_p9_interrupts(struct psi *psi) { struct proc_chip *c; bool is_dd2; u64 val; /* Reset irq handling and switch to ESB mode */ out_be64(psi->regs + PSIHB_INTERRUPT_CONTROL, PSIHB_IRQ_RESET); out_be64(psi->regs + PSIHB_INTERRUPT_CONTROL, 0); #define PSIHB_ESB_MMIO_DEFAULT 0x0060302031c0000ull /* Configure the CI BAR if necessary */ val = in_be64(psi->regs + PSIHB_ESB_CI_BASE); if (!(val & PSIHB_ESB_CI_VALID)) { val = PSIHB_ESB_MMIO_DEFAULT | PSIHB_ESB_CI_VALID; val |= (0x40000000000ull * (uint64_t)psi->chip_id); out_be64(psi->regs + PSIHB_ESB_CI_BASE, val); printf("PSI[0x%03x]: ESB MMIO invalid, reconfiguring...\n", psi->chip_id); } val = in_be64(psi->regs + PSIHB_ESB_CI_BASE); psi->esb_mmio = (void *)(val & ~PSIHB_ESB_CI_VALID); printf("PSI[0x%03x]: ESB MMIO at @%p\n", psi->chip_id, psi->esb_mmio); /* Grab and configure the notification port */ val = xive_get_notify_port(psi->chip_id, XIVE_HW_SRC_PSI); val |= PSIHB_ESB_NOTIF_VALID; out_be64(psi->regs + PSIHB_ESB_NOTIF_ADDR, val); /* Setup interrupt offset */ val = xive_get_notify_base(psi->interrupt); val <<= 32; out_be64(psi->regs + PSIHB_IVT_OFFSET, val); /* Register sources */ c = next_chip(NULL); is_dd2 = (c && c->ec_level >= 0x20); if (is_dd2) { prlog(PR_DEBUG, "PSI[0x%03x]: Interrupts sources registered for P9 DD2.x\n", psi->chip_id); xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS, 12, psi->esb_mmio, XIVE_SRC_LSI, psi, &psi_p9_irq_ops); } else { prlog(PR_DEBUG, "PSI[0x%03x]: Interrupts sources registered for P9 DD1.x\n", psi->chip_id); xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS, 12, psi->esb_mmio, XIVE_SRC_LSI, psi, &psi_p9_dd1_irq_ops); } } static void psi_init_interrupts(struct psi *psi) { /* Configure the interrupt BUID and mask it */ switch (proc_gen) { case proc_gen_p7: psi_init_p7_interrupt(psi); break; case proc_gen_p8: psi_init_p8_interrupts(psi); break; case proc_gen_p9: psi_init_p9_interrupts(psi); break; default: /* Unknown: just no interrupts */ prerror("PSI: Unknown interrupt type\n"); } } static void psi_activate_phb(struct psi *psi) { u64 reg; /* * Disable interrupt emission in the control register, * it will be re-enabled later, after the mailbox one * will have been enabled. */ reg = in_be64(psi->regs + PSIHB_CR); reg &= ~PSIHB_CR_FSP_IRQ_ENABLE; out_be64(psi->regs + PSIHB_CR, reg); /* Enable interrupts in the mask register. We enable everything * except for bit "FSP command error detected" which the doc * (P7 BookIV) says should be masked for normal ops. It also * seems to be masked under OPAL. */ reg = 0x0000010000100000ull; out_be64(psi->regs + PSIHB_SEMR, reg); #if 0 /* Dump the GXHB registers */ printf(" PSIHB_BBAR : %llx\n", in_be64(psi->regs + PSIHB_BBAR)); printf(" PSIHB_FSPBAR : %llx\n", in_be64(psi->regs + PSIHB_FSPBAR)); printf(" PSIHB_FSPMMR : %llx\n", in_be64(psi->regs + PSIHB_FSPMMR)); printf(" PSIHB_TAR : %llx\n", in_be64(psi->regs + PSIHB_TAR)); printf(" PSIHB_CR : %llx\n", in_be64(psi->regs + PSIHB_CR)); printf(" PSIHB_SEMR : %llx\n", in_be64(psi->regs + PSIHB_SEMR)); printf(" PSIHB_XIVR : %llx\n", in_be64(psi->regs + PSIHB_XIVR)); #endif } static void psi_create_p9_int_map(struct psi *psi, struct dt_node *np) { uint32_t map[P9_PSI_NUM_IRQS][4]; int i; for (i = 0; i < P9_PSI_NUM_IRQS; i++) { map[i][0] = i; map[i][1] = get_ics_phandle(); map[i][2] = psi->interrupt + i; map[i][3] = 1; } dt_add_property(np, "interrupt-map", map, sizeof(map)); } static void psi_create_mm_dtnode(struct psi *psi) { struct dt_node *np; uint64_t addr = (uint64_t)psi->regs; np = dt_new_addr(dt_root, "psi", addr); if (!np) return; /* Hard wire size to 4G */ dt_add_property_u64s(np, "reg", addr, 0x100000000ull); switch (proc_gen) { case proc_gen_p7: dt_add_property_strings(np, "compatible", "ibm,psi", "ibm,power7-psi"); break; case proc_gen_p8: dt_add_property_strings(np, "compatible", "ibm,psi", "ibm,power8-psi"); break; case proc_gen_p9: dt_add_property_strings(np, "compatible", "ibm,psi", "ibm,power9-psi"); psi_create_p9_int_map(psi, np); break; default: dt_add_property_strings(np, "compatible", "ibm,psi"); } dt_add_property_cells(np, "interrupt-parent", get_ics_phandle()); dt_add_property_cells(np, "interrupts", psi->interrupt, 1); dt_add_property_cells(np, "ibm,chip-id", psi->chip_id); } static struct psi *alloc_psi(struct proc_chip *chip, uint64_t base) { struct psi *psi; psi = zalloc(sizeof(struct psi)); if (!psi) { prerror("PSI: Could not allocate memory\n"); return NULL; } psi->xscom_base = base; psi->chip_id = chip->id; return psi; } static struct psi *psi_probe_p7(struct proc_chip *chip, u64 base) { struct psi *psi = NULL; uint64_t rc, val; rc = xscom_read(chip->id, base + PSIHB_XSCOM_P7_HBBAR, &val); if (rc) { prerror("PSI: Error %llx reading PSIHB BAR on chip %d\n", rc, chip->id); return NULL; } if (val & PSIHB_XSCOM_P7_HBBAR_EN) { psi = alloc_psi(chip, base); if (!psi) return NULL; rc = val >> 36; /* Bits 0:1 = 0x00; 2:27 Bridge BAR... */ rc <<= 20; /* ... corresponds to bits 18:43 of base addr */ psi->regs = (void *)rc; psi->interrupt = get_psi_interrupt(chip->id); } else printf("PSI[0x%03x]: Working link not found\n", chip->id); return psi; } static struct psi *psi_probe_p8(struct proc_chip *chip, u64 base) { struct psi *psi = NULL; uint64_t rc, val; rc = xscom_read(chip->id, base + PSIHB_XSCOM_P8_BASE, &val); if (rc) { prerror("PSI[0x%03x]: Error %llx reading PSIHB BAR\n", chip->id, rc); return NULL; } if (val & PSIHB_XSCOM_P8_HBBAR_EN) { psi = alloc_psi(chip, base); if (!psi) return NULL; psi->regs = (void *)(val & ~PSIHB_XSCOM_P8_HBBAR_EN); psi->interrupt = get_psi_interrupt(chip->id); } else printf("PSI[0x%03x]: Working chip not found\n", chip->id); return psi; } static struct psi *psi_probe_p9(struct proc_chip *chip, u64 base) { struct psi *psi = NULL; uint64_t rc, val; rc = xscom_read(chip->id, base + PSIHB_XSCOM_P9_BASE, &val); if (rc) { prerror("PSI[0x%03x]: Error %llx reading PSIHB BAR\n", chip->id, rc); return NULL; } if (!val & PSIHB_XSCOM_P9_HBBAR_EN) { prerror("PSI[0x%03x]: PSIHB BAR Disabled,fixing up (%016llx)\n", chip->id, val); #define PSIHB_PSI_MMIO_DEFAULT 0x006030203000000ull val = PSIHB_PSI_MMIO_DEFAULT | PSIHB_XSCOM_P9_HBBAR_EN; val |= (0x40000000000ull * (uint64_t)chip->id); xscom_write(chip->id, base + PSIHB_XSCOM_P9_BASE, val); } psi = alloc_psi(chip, base); if (!psi) return NULL; psi->regs = (void *)(val & ~PSIHB_XSCOM_P9_HBBAR_EN); psi->interrupt = xive_alloc_hw_irqs(chip->id, P9_PSI_NUM_IRQS, 16); return psi; } static bool psi_init_psihb(struct dt_node *psihb) { uint32_t chip_id = dt_get_chip_id(psihb); struct proc_chip *chip = get_chip(chip_id); struct psi *psi = NULL; u64 base, val; if (!chip) { prerror("PSI: Can't find chip!\n"); return false; } base = dt_get_address(psihb, 0, NULL); if (dt_node_is_compatible(psihb, "ibm,power7-psihb-x")) psi = psi_probe_p7(chip, base); else if (dt_node_is_compatible(psihb, "ibm,power8-psihb-x")) psi = psi_probe_p8(chip, base); else if (dt_node_is_compatible(psihb, "ibm,power9-psihb-x")) psi = psi_probe_p9(chip, base); else { prerror("PSI: Unknown processor type\n"); return false; } if (!psi) return false; list_add(&psis, &psi->list); val = in_be64(psi->regs + PSIHB_CR); if (val & PSIHB_CR_FSP_LINK_ACTIVE) { lock(&psi_lock); psi->active = true; unlock(&psi_lock); } chip->psi = psi; if (dt_has_node_property(psihb, "no-lpc-interrupts", NULL)) psi->no_lpc_irqs = true; psi_activate_phb(psi); psi_init_interrupts(psi); psi_create_mm_dtnode(psi); printf("PSI[0x%03x]: Found PSI bridge [active=%d]\n", psi->chip_id, psi->active); return true; } void psi_fsp_link_in_use(struct psi *psi __unused) { static bool poller_created = false; /* Do this once only */ if (!poller_created) { poller_created = true; opal_add_poller(psi_link_poll, NULL); } } struct psi *psi_find_functional_chip(void) { return list_top(&psis, struct psi, list); } void psi_init(void) { struct dt_node *np; dt_for_each_compatible(dt_root, np, "ibm,psihb-x") psi_init_psihb(np); }