aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/pci-opal.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/pci-opal.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/pci-opal.c')
-rw-r--r--roms/skiboot/core/pci-opal.c1135
1 files changed, 1135 insertions, 0 deletions
diff --git a/roms/skiboot/core/pci-opal.c b/roms/skiboot/core/pci-opal.c
new file mode 100644
index 000000000..aa375c6aa
--- /dev/null
+++ b/roms/skiboot/core/pci-opal.c
@@ -0,0 +1,1135 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * PCIe OPAL Calls
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <opal-api.h>
+#include <pci.h>
+#include <pci-cfg.h>
+#include <pci-slot.h>
+#include <opal-msg.h>
+#include <timebase.h>
+#include <timer.h>
+
+#define OPAL_PCICFG_ACCESS_READ(op, cb, type) \
+static int64_t opal_pci_config_##op(uint64_t phb_id, \
+ uint64_t bus_dev_func, \
+ uint64_t offset, type data) \
+{ \
+ struct phb *phb = pci_get_phb(phb_id); \
+ int64_t rc; \
+ \
+ if (!opal_addr_valid((void *)data)) \
+ return OPAL_PARAMETER; \
+ \
+ if (!phb) \
+ return OPAL_PARAMETER; \
+ phb_lock(phb); \
+ rc = phb->ops->cfg_##cb(phb, bus_dev_func, offset, data); \
+ phb_unlock(phb); \
+ \
+ return rc; \
+}
+
+#define OPAL_PCICFG_ACCESS_WRITE(op, cb, type) \
+static int64_t opal_pci_config_##op(uint64_t phb_id, \
+ uint64_t bus_dev_func, \
+ uint64_t offset, type data) \
+{ \
+ struct phb *phb = pci_get_phb(phb_id); \
+ int64_t rc; \
+ \
+ if (!phb) \
+ return OPAL_PARAMETER; \
+ phb_lock(phb); \
+ rc = phb->ops->cfg_##cb(phb, bus_dev_func, offset, data); \
+ phb_unlock(phb); \
+ \
+ return rc; \
+}
+
+OPAL_PCICFG_ACCESS_READ(read_byte, read8, uint8_t *)
+OPAL_PCICFG_ACCESS_READ(read_half_word, read16, uint16_t *)
+OPAL_PCICFG_ACCESS_READ(read_word, read32, uint32_t *)
+OPAL_PCICFG_ACCESS_WRITE(write_byte, write8, uint8_t)
+OPAL_PCICFG_ACCESS_WRITE(write_half_word, write16, uint16_t)
+OPAL_PCICFG_ACCESS_WRITE(write_word, write32, uint32_t)
+
+static int64_t opal_pci_config_read_half_word_be(uint64_t phb_id,
+ uint64_t bus_dev_func,
+ uint64_t offset,
+ __be16 *__data)
+{
+ uint16_t data;
+ int64_t rc;
+
+ rc = opal_pci_config_read_half_word(phb_id, bus_dev_func, offset, &data);
+ *__data = cpu_to_be16(data);
+
+ return rc;
+}
+
+static int64_t opal_pci_config_read_word_be(uint64_t phb_id,
+ uint64_t bus_dev_func,
+ uint64_t offset,
+ __be32 *__data)
+{
+ uint32_t data;
+ int64_t rc;
+
+ rc = opal_pci_config_read_word(phb_id, bus_dev_func, offset, &data);
+ *__data = cpu_to_be32(data);
+
+ return rc;
+}
+
+
+opal_call(OPAL_PCI_CONFIG_READ_BYTE, opal_pci_config_read_byte, 4);
+opal_call(OPAL_PCI_CONFIG_READ_HALF_WORD, opal_pci_config_read_half_word_be, 4);
+opal_call(OPAL_PCI_CONFIG_READ_WORD, opal_pci_config_read_word_be, 4);
+opal_call(OPAL_PCI_CONFIG_WRITE_BYTE, opal_pci_config_write_byte, 4);
+opal_call(OPAL_PCI_CONFIG_WRITE_HALF_WORD, opal_pci_config_write_half_word, 4);
+opal_call(OPAL_PCI_CONFIG_WRITE_WORD, opal_pci_config_write_word, 4);
+
+static struct lock opal_eeh_evt_lock = LOCK_UNLOCKED;
+static uint64_t opal_eeh_evt = 0;
+
+void opal_pci_eeh_set_evt(uint64_t phb_id)
+{
+ lock(&opal_eeh_evt_lock);
+ opal_eeh_evt |= 1ULL << phb_id;
+ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, OPAL_EVENT_PCI_ERROR);
+ unlock(&opal_eeh_evt_lock);
+}
+
+void opal_pci_eeh_clear_evt(uint64_t phb_id)
+{
+ lock(&opal_eeh_evt_lock);
+ opal_eeh_evt &= ~(1ULL << phb_id);
+ if (!opal_eeh_evt)
+ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, 0);
+ unlock(&opal_eeh_evt_lock);
+}
+
+static int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number,
+ uint8_t *freeze_state,
+ __be16 *__pci_error_type,
+ __be64 *__phb_status)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ uint16_t pci_error_type;
+ int64_t rc;
+
+ if (!opal_addr_valid(freeze_state) || !opal_addr_valid(__pci_error_type)
+ || !opal_addr_valid(__phb_status))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->eeh_freeze_status)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+
+ if (__phb_status)
+ prlog(PR_ERR, "PHB#%04llx: %s: deprecated PHB status\n",
+ phb_id, __func__);
+
+ rc = phb->ops->eeh_freeze_status(phb, pe_number, freeze_state,
+ &pci_error_type, NULL);
+ *__pci_error_type = cpu_to_be16(pci_error_type);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_EEH_FREEZE_STATUS, opal_pci_eeh_freeze_status, 5);
+
+static int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number,
+ uint64_t eeh_action_token)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->eeh_freeze_clear)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->eeh_freeze_clear(phb, pe_number, eeh_action_token);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_EEH_FREEZE_CLEAR, opal_pci_eeh_freeze_clear, 3);
+
+static int64_t opal_pci_eeh_freeze_set(uint64_t phb_id, uint64_t pe_number,
+ uint64_t eeh_action_token)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->eeh_freeze_set)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->eeh_freeze_set(phb, pe_number, eeh_action_token);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_EEH_FREEZE_SET, opal_pci_eeh_freeze_set, 3);
+
+static int64_t opal_pci_err_inject(uint64_t phb_id, uint64_t pe_number,
+ uint32_t type, uint32_t func,
+ uint64_t addr, uint64_t mask)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops || !phb->ops->err_inject)
+ return OPAL_UNSUPPORTED;
+
+ if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR &&
+ type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64)
+ return OPAL_PARAMETER;
+
+ phb_lock(phb);
+ rc = phb->ops->err_inject(phb, pe_number, type, func, addr, mask);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_ERR_INJECT, opal_pci_err_inject, 6);
+
+static int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type,
+ uint16_t window_num, uint16_t enable)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->phb_mmio_enable)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->phb_mmio_enable(phb, window_type, window_num, enable);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_PHB_MMIO_ENABLE, opal_pci_phb_mmio_enable, 4);
+
+static int64_t opal_pci_set_phb_mem_window(uint64_t phb_id,
+ uint16_t window_type,
+ uint16_t window_num,
+ uint64_t addr,
+ uint64_t pci_addr,
+ uint64_t size)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_phb_mem_window)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_phb_mem_window(phb, window_type, window_num,
+ addr, pci_addr, size);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_PHB_MEM_WINDOW, opal_pci_set_phb_mem_window, 6);
+
+static int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint64_t pe_number,
+ uint16_t window_type,
+ uint16_t window_num,
+ uint16_t segment_num)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->map_pe_mmio_window)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->map_pe_mmio_window(phb, pe_number, window_type,
+ window_num, segment_num);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_MAP_PE_MMIO_WINDOW, opal_pci_map_pe_mmio_window, 5);
+
+static int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number,
+ uint64_t bus_dev_func, uint8_t bus_compare,
+ uint8_t dev_compare, uint8_t func_compare,
+ uint8_t pe_action)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_pe)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_pe(phb, pe_number, bus_dev_func, bus_compare,
+ dev_compare, func_compare, pe_action);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_PE, opal_pci_set_pe, 7);
+
+static int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe,
+ uint32_t child_pe, uint8_t state)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_peltv)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_peltv(phb, parent_pe, child_pe, state);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_PELTV, opal_pci_set_peltv, 4);
+
+static int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number,
+ uint64_t pe_number)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_mve)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_mve(phb, mve_number, pe_number);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_MVE, opal_pci_set_mve, 3);
+
+static int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number,
+ uint32_t state)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_mve_enable)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_mve_enable(phb, mve_number, state);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_MVE_ENABLE, opal_pci_set_mve_enable, 3);
+
+static int64_t opal_pci_msi_eoi(uint64_t phb_id,
+ uint32_t hwirq)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->pci_msi_eoi)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->pci_msi_eoi(phb, hwirq);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_MSI_EOI, opal_pci_msi_eoi, 2);
+
+static int64_t opal_pci_tce_kill(uint64_t phb_id,
+ uint32_t kill_type,
+ uint64_t pe_number, uint32_t tce_size,
+ uint64_t dma_addr, uint32_t npages)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->tce_kill)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->tce_kill(phb, kill_type, pe_number, tce_size,
+ dma_addr, npages);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_TCE_KILL, opal_pci_tce_kill, 6);
+
+static int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint64_t pe_number,
+ uint32_t xive_num)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_xive_pe)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->set_xive_pe(phb, pe_number, xive_num);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_XIVE_PE, opal_pci_set_xive_pe, 3);
+
+static int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number,
+ uint32_t xive_num, uint8_t msi_range,
+ __be32 *__msi_address, __be32 *__message_data)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ uint32_t msi_address;
+ uint32_t message_data;
+ int64_t rc;
+
+ if (!opal_addr_valid(__msi_address) || !opal_addr_valid(__message_data))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->get_msi_32)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->get_msi_32(phb, mve_number, xive_num, msi_range,
+ &msi_address, &message_data);
+ phb_unlock(phb);
+
+ *__msi_address = cpu_to_be32(msi_address);
+ *__message_data = cpu_to_be32(message_data);
+
+ return rc;
+}
+opal_call(OPAL_GET_MSI_32, opal_get_msi_32, 6);
+
+static int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number,
+ uint32_t xive_num, uint8_t msi_range,
+ __be64 *__msi_address, __be32 *__message_data)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ uint64_t msi_address;
+ uint32_t message_data;
+ int64_t rc;
+
+ if (!opal_addr_valid(__msi_address) || !opal_addr_valid(__message_data))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->get_msi_64)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->get_msi_64(phb, mve_number, xive_num, msi_range,
+ &msi_address, &message_data);
+ phb_unlock(phb);
+
+ *__msi_address = cpu_to_be64(msi_address);
+ *__message_data = cpu_to_be32(message_data);
+
+ return rc;
+}
+opal_call(OPAL_GET_MSI_64, opal_get_msi_64, 6);
+
+static int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint64_t pe_number,
+ uint16_t window_id,
+ uint16_t tce_levels,
+ uint64_t tce_table_addr,
+ uint64_t tce_table_size,
+ uint64_t tce_page_size)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->map_pe_dma_window)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->map_pe_dma_window(phb, pe_number, window_id,
+ tce_levels, tce_table_addr,
+ tce_table_size, tce_page_size);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW, opal_pci_map_pe_dma_window, 7);
+
+static int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id,
+ uint64_t pe_number,
+ uint16_t window_id,
+ uint64_t pci_start_addr,
+ uint64_t pci_mem_size)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->map_pe_dma_window_real)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->map_pe_dma_window_real(phb, pe_number, window_id,
+ pci_start_addr, pci_mem_size);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW_REAL, opal_pci_map_pe_dma_window_real, 5);
+
+static int64_t opal_phb_set_option(uint64_t phb_id, uint64_t opt,
+ uint64_t setting)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+
+ if (!phb->ops->set_option)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = phb->ops->set_option(phb, opt, setting);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PHB_SET_OPTION, opal_phb_set_option, 3);
+
+static int64_t opal_phb_get_option(uint64_t phb_id, uint64_t opt,
+ __be64 *setting)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb || !setting)
+ return OPAL_PARAMETER;
+
+ if (!phb->ops->get_option)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = phb->ops->get_option(phb, opt, setting);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PHB_GET_OPTION, opal_phb_get_option, 3);
+
+static int64_t opal_pci_reset(uint64_t id, uint8_t reset_scope,
+ uint8_t assert_state)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ int64_t rc = OPAL_SUCCESS;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (assert_state != OPAL_ASSERT_RESET &&
+ assert_state != OPAL_DEASSERT_RESET)
+ return OPAL_PARAMETER;
+
+ phb_lock(phb);
+
+ switch(reset_scope) {
+ case OPAL_RESET_PHB_COMPLETE:
+ /* Complete reset is applicable to PHB slot only */
+ if (!slot->ops.creset || slot->pd) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = slot->ops.creset(slot);
+ if (rc < 0)
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on complete reset\n",
+ slot->id, rc);
+ break;
+ case OPAL_RESET_PCI_FUNDAMENTAL:
+ if (!slot->ops.freset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
+ /* We need do nothing on deassert time */
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = slot->ops.freset(slot);
+ if (rc < 0)
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on fundamental reset\n",
+ slot->id, rc);
+ break;
+ case OPAL_RESET_PCI_HOT:
+ if (!slot->ops.hreset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
+ /* We need do nothing on deassert time */
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = slot->ops.hreset(slot);
+ if (rc < 0)
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on hot reset\n",
+ slot->id, rc);
+ break;
+ case OPAL_RESET_PCI_IODA_TABLE:
+ /* It's allowed on PHB slot only */
+ if (slot->pd || !phb->ops || !phb->ops->ioda_reset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = phb->ops->ioda_reset(phb, true);
+ break;
+ case OPAL_RESET_PHB_ERROR:
+ /* It's allowed on PHB slot only */
+ if (slot->pd || !phb->ops || !phb->ops->papr_errinjct_reset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = phb->ops->papr_errinjct_reset(phb);
+ break;
+ default:
+ rc = OPAL_UNSUPPORTED;
+ }
+ phb_unlock(phb);
+
+ return (rc > 0) ? tb_to_msecs(rc) : rc;
+}
+opal_call(OPAL_PCI_RESET, opal_pci_reset, 3);
+
+static int64_t opal_pci_reinit(uint64_t phb_id,
+ uint64_t reinit_scope,
+ uint64_t data)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops || !phb->ops->pci_reinit)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = phb->ops->pci_reinit(phb, reinit_scope, data);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_REINIT, opal_pci_reinit, 3);
+
+static int64_t opal_pci_poll(uint64_t id)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ int64_t rc;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (!slot->ops.run_sm)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = slot->ops.run_sm(slot);
+ phb_unlock(phb);
+
+ /* Return milliseconds for caller to sleep: round up */
+ if (rc > 0) {
+ rc = tb_to_msecs(rc);
+ if (rc == 0)
+ rc = 1;
+ }
+
+ return rc;
+}
+opal_call(OPAL_PCI_POLL, opal_pci_poll, 1);
+
+static int64_t opal_pci_get_presence_state(uint64_t id, uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ uint8_t *presence = (uint8_t *)data;
+ int64_t rc;
+
+ if (!opal_addr_valid(presence))
+ return OPAL_PARAMETER;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (!slot->ops.get_presence_state)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = slot->ops.get_presence_state(slot, presence);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_GET_PRESENCE_STATE, opal_pci_get_presence_state, 2);
+
+static int64_t opal_pci_get_power_state(uint64_t id, uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ uint8_t *power_state = (uint8_t *)data;
+ int64_t rc;
+
+ if (!opal_addr_valid(power_state))
+ return OPAL_PARAMETER;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (!slot->ops.get_power_state)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = slot->ops.get_power_state(slot, power_state);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_GET_POWER_STATE, opal_pci_get_power_state, 2);
+
+static u32 get_slot_phandle(struct pci_slot *slot)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+
+ if (pd)
+ return pd->dn->phandle;
+ else
+ return phb->dt_node->phandle;
+}
+
+static void rescan_slot_devices(struct pci_slot *slot)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+
+ /*
+ * prepare_link_change() is called (if needed) by the state
+ * machine during the slot reset or link polling
+ */
+ if (phb->phb_type != phb_type_npu_v2_opencapi) {
+ pci_scan_bus(phb, pd->secondary_bus,
+ pd->subordinate_bus, &pd->children, pd, true);
+ pci_add_device_nodes(phb, &pd->children, pd->dn,
+ &phb->lstate, 0);
+ } else {
+ pci_scan_bus(phb, 0, 0xff, &phb->devices, NULL, true);
+ pci_add_device_nodes(phb, &phb->devices,
+ phb->dt_node, &phb->lstate, 0);
+ phb->ops->phb_final_fixup(phb);
+ }
+}
+
+static void remove_slot_devices(struct pci_slot *slot)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+
+ if (phb->phb_type != phb_type_npu_v2_opencapi)
+ pci_remove_bus(phb, &pd->children);
+ else
+ pci_remove_bus(phb, &phb->devices);
+}
+
+static void link_up_timer(struct timer *t, void *data,
+ uint64_t now __unused)
+{
+ struct pci_slot *slot = data;
+ struct phb *phb = slot->phb;
+ uint8_t link;
+ int64_t rc = 0;
+
+ if (!phb_try_lock(phb)) {
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ return;
+ }
+
+ rc = slot->ops.run_sm(slot);
+ if (rc < 0)
+ goto out;
+ if (rc > 0) {
+ schedule_timer(t, rc);
+ phb_unlock(phb);
+ return;
+ }
+
+ if (slot->ops.get_link_state(slot, &link) != OPAL_SUCCESS)
+ link = 0;
+ if (!link) {
+ rc = OPAL_HARDWARE;
+ goto out;
+ }
+
+ rescan_slot_devices(slot);
+out:
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ cpu_to_be64(slot->async_token),
+ cpu_to_be64(get_slot_phandle(slot)),
+ cpu_to_be64(slot->power_state),
+ rc <= 0 ? cpu_to_be64(rc) : cpu_to_be64(OPAL_BUSY));
+ phb_unlock(phb);
+}
+
+static bool training_needed(struct pci_slot *slot)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+
+ /* only for opencapi slots for now */
+ if (!pd && phb->phb_type == phb_type_npu_v2_opencapi)
+ return true;
+ return false;
+}
+
+static void wait_for_link_up_and_rescan(struct pci_slot *slot)
+{
+ int64_t rc = 1;
+
+ /*
+ * Links for PHB slots need to be retrained by triggering a
+ * fundamental reset. Other slots also need to be tested for
+ * readiness
+ */
+ if (training_needed(slot)) {
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ rc = slot->ops.freset(slot);
+ if (rc < 0) {
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ cpu_to_be64(slot->async_token),
+ cpu_to_be64(get_slot_phandle(slot)),
+ cpu_to_be64(slot->power_state),
+ cpu_to_be64(rc))
+ return;
+ }
+ } else {
+ pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL);
+ rc = msecs_to_tb(20);
+ }
+ init_timer(&slot->timer, link_up_timer, slot);
+ schedule_timer(&slot->timer, rc);
+}
+
+static void set_power_timer(struct timer *t __unused, void *data,
+ uint64_t now __unused)
+{
+ struct pci_slot *slot = data;
+ struct phb *phb = slot->phb;
+
+ if (!phb_try_lock(phb)) {
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ return;
+ }
+
+ switch (slot->state) {
+ case PCI_SLOT_STATE_SPOWER_START:
+ if (slot->retries-- == 0) {
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ cpu_to_be64(slot->async_token),
+ cpu_to_be64(get_slot_phandle(slot)),
+ cpu_to_be64(slot->power_state),
+ cpu_to_be64(OPAL_BUSY));
+ } else {
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ }
+
+ break;
+ case PCI_SLOT_STATE_SPOWER_DONE:
+ if (slot->power_state == OPAL_PCI_SLOT_POWER_OFF) {
+ remove_slot_devices(slot);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ cpu_to_be64(slot->async_token),
+ cpu_to_be64(get_slot_phandle(slot)),
+ cpu_to_be64(OPAL_PCI_SLOT_POWER_OFF),
+ cpu_to_be64(OPAL_SUCCESS));
+ break;
+ }
+
+ /* Power on */
+ wait_for_link_up_and_rescan(slot);
+ break;
+ default:
+ prlog(PR_ERR, "PCI SLOT %016llx: Unexpected state 0x%08x\n",
+ slot->id, slot->state);
+ }
+ phb_unlock(phb);
+}
+
+static int64_t opal_pci_set_power_state(uint64_t async_token,
+ uint64_t id,
+ uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ struct pci_device *pd = slot ? slot->pd : NULL;
+ uint8_t *state = (uint8_t *)data;
+ int64_t rc;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+
+ if (!opal_addr_valid(state))
+ return OPAL_PARAMETER;
+
+ phb_lock(phb);
+ switch (*state) {
+ case OPAL_PCI_SLOT_POWER_OFF:
+ if (!slot->ops.prepare_link_change ||
+ !slot->ops.set_power_state) {
+ phb_unlock(phb);
+ return OPAL_UNSUPPORTED;
+ }
+
+ slot->async_token = async_token;
+ slot->ops.prepare_link_change(slot, false);
+ rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+ break;
+ case OPAL_PCI_SLOT_POWER_ON:
+ if (!slot->ops.set_power_state ||
+ !slot->ops.get_link_state) {
+ phb_unlock(phb);
+ return OPAL_UNSUPPORTED;
+ }
+
+ slot->async_token = async_token;
+ rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON);
+ break;
+ case OPAL_PCI_SLOT_OFFLINE:
+ if (!pd) {
+ phb_unlock(phb);
+ return OPAL_PARAMETER;
+ }
+
+ pci_remove_bus(phb, &pd->children);
+ phb_unlock(phb);
+ return OPAL_SUCCESS;
+ case OPAL_PCI_SLOT_ONLINE:
+ if (!pd) {
+ phb_unlock(phb);
+ return OPAL_PARAMETER;
+ }
+ pci_scan_bus(phb, pd->secondary_bus, pd->subordinate_bus,
+ &pd->children, pd, true);
+ pci_add_device_nodes(phb, &pd->children, pd->dn,
+ &phb->lstate, 0);
+ phb_unlock(phb);
+ return OPAL_SUCCESS;
+ default:
+ rc = OPAL_PARAMETER;
+ }
+
+ /*
+ * OPAL_ASYNC_COMPLETION is returned when delay is needed to change
+ * the power state in the backend. When it can be finished without
+ * delay, OPAL_SUCCESS is returned. The PCI topology needs to be
+ * updated in both cases.
+ */
+ if (rc == OPAL_ASYNC_COMPLETION) {
+ slot->retries = 500;
+ init_timer(&slot->timer, set_power_timer, slot);
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ } else if (rc == OPAL_SUCCESS) {
+ if (*state == OPAL_PCI_SLOT_POWER_OFF) {
+ remove_slot_devices(slot);
+ } else {
+ wait_for_link_up_and_rescan(slot);
+ rc = OPAL_ASYNC_COMPLETION;
+ }
+ }
+
+ phb_unlock(phb);
+ return rc;
+}
+opal_call(OPAL_PCI_SET_POWER_STATE, opal_pci_set_power_state, 3);
+
+static int64_t opal_pci_get_phb_diag_data2(uint64_t phb_id,
+ void *diag_buffer,
+ uint64_t diag_buffer_len)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!opal_addr_valid(diag_buffer))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->get_diag_data2)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+ rc = phb->ops->get_diag_data2(phb, diag_buffer, diag_buffer_len);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_GET_PHB_DIAG_DATA2, opal_pci_get_phb_diag_data2, 3);
+
+static int64_t opal_pci_next_error(uint64_t phb_id, __be64 *__first_frozen_pe,
+ __be16 *__pci_error_type, __be16 *__severity)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ uint64_t first_frozen_pe;
+ uint16_t pci_error_type;
+ uint16_t severity;
+ int64_t rc;
+
+ if (!opal_addr_valid(__first_frozen_pe) ||
+ !opal_addr_valid(__pci_error_type) || !opal_addr_valid(__severity))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->next_error)
+ return OPAL_UNSUPPORTED;
+ phb_lock(phb);
+
+ opal_pci_eeh_clear_evt(phb_id);
+ rc = phb->ops->next_error(phb, &first_frozen_pe, &pci_error_type,
+ &severity);
+ phb_unlock(phb);
+
+ *__first_frozen_pe = cpu_to_be64(first_frozen_pe);
+ *__pci_error_type = cpu_to_be16(pci_error_type);
+ *__severity = cpu_to_be16(severity);
+
+ return rc;
+}
+opal_call(OPAL_PCI_NEXT_ERROR, opal_pci_next_error, 4);
+
+static int64_t opal_pci_set_phb_capi_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_capi_mode)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = phb->ops->set_capi_mode(phb, mode, pe_number);
+ phb_unlock(phb);
+ return rc;
+}
+opal_call(OPAL_PCI_SET_PHB_CAPI_MODE, opal_pci_set_phb_capi_mode, 3);
+
+static int64_t opal_pci_set_p2p(uint64_t phbid_init, uint64_t phbid_target,
+ uint64_t desc, uint16_t pe_number)
+{
+ struct phb *phb_init = pci_get_phb(phbid_init);
+ struct phb *phb_target = pci_get_phb(phbid_target);
+
+ if (!phb_init || !phb_target)
+ return OPAL_PARAMETER;
+ /*
+ * Having the 2 devices under the same PHB may require tuning
+ * the configuration of intermediate switch(es), more easily
+ * done from linux. And it shouldn't require a PHB config
+ * change.
+ * Return an error for the time being.
+ */
+ if (phb_init == phb_target)
+ return OPAL_UNSUPPORTED;
+ if (!phb_init->ops->set_p2p || !phb_target->ops->set_p2p)
+ return OPAL_UNSUPPORTED;
+ /*
+ * Loads would be supported on p9 if the 2 devices are under
+ * the same PHB, but we ruled it out above.
+ */
+ if (desc & OPAL_PCI_P2P_LOAD)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb_init);
+ phb_init->ops->set_p2p(phb_init, OPAL_PCI_P2P_INITIATOR, desc,
+ pe_number);
+ phb_unlock(phb_init);
+
+ phb_lock(phb_target);
+ phb_target->ops->set_p2p(phb_target, OPAL_PCI_P2P_TARGET, desc,
+ pe_number);
+ phb_unlock(phb_target);
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_PCI_SET_P2P, opal_pci_set_p2p, 4);
+
+static int64_t opal_pci_get_pbcq_tunnel_bar(uint64_t phb_id, __be64 *__addr)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ uint64_t addr;
+
+ if (!opal_addr_valid(__addr))
+ return OPAL_PARAMETER;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->get_tunnel_bar)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ phb->ops->get_tunnel_bar(phb, &addr);
+ phb_unlock(phb);
+
+ *__addr = cpu_to_be64(addr);
+
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_PCI_GET_PBCQ_TUNNEL_BAR, opal_pci_get_pbcq_tunnel_bar, 2);
+
+static int64_t opal_pci_set_pbcq_tunnel_bar(uint64_t phb_id, uint64_t addr)
+{
+ struct phb *phb = pci_get_phb(phb_id);
+ int64_t rc;
+
+ if (!phb)
+ return OPAL_PARAMETER;
+ if (!phb->ops->set_tunnel_bar)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = phb->ops->set_tunnel_bar(phb, addr);
+ phb_unlock(phb);
+ return rc;
+}
+opal_call(OPAL_PCI_SET_PBCQ_TUNNEL_BAR, opal_pci_set_pbcq_tunnel_bar, 2);