aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/pau.c157
-rw-r--r--include/pau-regs.h12
-rw-r--r--include/pau.h2
3 files changed, 171 insertions, 0 deletions
diff --git a/hw/pau.c b/hw/pau.c
index 36c5f224..eb31715 100644
--- a/hw/pau.c
+++ b/hw/pau.c
@@ -5,6 +5,7 @@
#include <interrupts.h>
#include <pci-slot.h>
#include <phys-map.h>
+#include <xive.h>
#include <pau.h>
#include <pau-regs.h>
#include <xscom-p10-regs.h>
@@ -237,6 +238,15 @@ static int pau_opencapi_set_fence_control(struct pau_dev *dev,
return OPAL_HARDWARE;
}
+#define PAU_DEV_STATUS_BROKEN 0x1
+
+static void pau_opencapi_set_broken(struct pau_dev *dev)
+{
+ PAUDEVDBG(dev, "Update status to broken\n");
+
+ dev->status = PAU_DEV_STATUS_BROKEN;
+}
+
static void pau_opencapi_mask_firs(struct pau *pau)
{
uint64_t reg, val;
@@ -310,6 +320,113 @@ static void pau_opencapi_assign_bars(struct pau *pau)
}
}
+static uint64_t pau_opencapi_ipi_attributes(struct irq_source *is,
+ uint32_t isn)
+{
+ struct pau *pau = is->data;
+ uint32_t level = isn - pau->irq_base;
+
+ if (level >= 37 && level <= 40) {
+ /* level 37-40: OTL/XSL interrupt */
+ return IRQ_ATTR_TARGET_OPAL |
+ IRQ_ATTR_TARGET_RARE |
+ IRQ_ATTR_TYPE_MSI;
+ }
+
+ return IRQ_ATTR_TARGET_LINUX;
+}
+
+static void pau_opencapi_ipi_interrupt(struct irq_source *is,
+ uint32_t isn)
+{
+ struct pau *pau = is->data;
+ uint32_t level = isn - pau->irq_base;
+ struct pau_dev *dev;
+
+ switch (level) {
+ case 37 ... 40:
+ pau_for_each_opencapi_dev(dev, pau)
+ pau_opencapi_set_broken(dev);
+
+ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR,
+ OPAL_EVENT_PCI_ERROR);
+ break;
+ default:
+ PAUERR(pau, "Received unknown interrupt %d\n", level);
+ return;
+ }
+}
+
+#define PAU_IRQ_LEVELS 60
+
+static char *pau_opencapi_ipi_name(struct irq_source *is, uint32_t isn)
+{
+ struct pau *pau = is->data;
+ uint32_t level = isn - pau->irq_base;
+
+ switch (level) {
+ case 0 ... 19:
+ return strdup("Reserved");
+ case 20:
+ return strdup("An error event related to PAU CQ functions");
+ case 21:
+ return strdup("An error event related to PAU MISC functions");
+ case 22 ... 34:
+ return strdup("Reserved");
+ case 35:
+ return strdup("Translation failure for OCAPI link 0");
+ case 36:
+ return strdup("Translation failure for OCAPI link 1");
+ case 37:
+ return strdup("An error event related to OTL for link 0");
+ case 38:
+ return strdup("An error event related to OTL for link 1");
+ case 39:
+ return strdup("An error event related to XSL for link 0");
+ case 40:
+ return strdup("An error event related to XSL for link 1");
+ case 41 ... 59:
+ return strdup("Reserved");
+ }
+
+ return strdup("Unknown");
+}
+
+static const struct irq_source_ops pau_opencapi_ipi_ops = {
+ .attributes = pau_opencapi_ipi_attributes,
+ .interrupt = pau_opencapi_ipi_interrupt,
+ .name = pau_opencapi_ipi_name,
+};
+
+static void pau_opencapi_setup_irqs(struct pau *pau)
+{
+ uint64_t reg, val;
+ uint32_t base;
+
+ base = xive2_alloc_ipi_irqs(pau->chip_id, PAU_IRQ_LEVELS, 64);
+ if (base == XIVE_IRQ_ERROR) {
+ PAUERR(pau, "Failed to allocate interrupt sources\n");
+ return;
+ }
+
+ xive2_register_ipi_source(base, PAU_IRQ_LEVELS, pau, &pau_opencapi_ipi_ops);
+
+ /* Set IPI configuration */
+ reg = PAU_MISC_CONFIG;
+ val = pau_read(pau, reg);
+ val = SETFIELD(PAU_MISC_CONFIG_IPI_PS, val, PAU_MISC_CONFIG_IPI_PS_64K);
+ val = SETFIELD(PAU_MISC_CONFIG_IPI_OS, val, PAU_MISC_CONFIG_IPI_OS_AIX);
+ pau_write(pau, reg, val);
+
+ /* Set IRQ base */
+ reg = PAU_MISC_INT_BAR;
+ val = SETFIELD(PAU_MISC_INT_BAR_ADDR, 0ull,
+ (uint64_t)xive2_get_trigger_port(base) >> 12);
+ pau_write(pau, reg, val);
+
+ pau->irq_base = base;
+}
+
static void pau_opencapi_enable_bars(struct pau_dev *dev, bool enable)
{
struct pau *pau = dev->pau;
@@ -769,6 +886,41 @@ static void pau_opencapi_address_translation_config(struct pau_dev *dev)
/* XSL_GP - use defaults */
}
+static void pau_opencapi_enable_interrupt_on_error(struct pau_dev *dev)
+{
+ struct pau *pau = dev->pau;
+ uint64_t reg, val;
+
+ PAUDEVDBG(dev, "Enable Interrupt-on-error\n");
+
+ /* translation fault */
+ reg = PAU_MISC_INT_2_CONFIG;
+ val = pau_read(pau, reg);
+ val |= PAU_MISC_INT_2_CONFIG_XFAULT_2_5(dev->index);
+ pau_write(pau, reg, val);
+
+ /* freeze disable */
+ reg = PAU_MISC_FREEZE_1_CONFIG;
+ val = pau_read(pau, reg);
+ val &= ~PAU_FIR1_NDL_BRICKS_0_5;
+ val &= ~PAU_FIR1_NDL_BRICKS_6_11;
+ pau_write(pau, reg, val);
+
+ /* fence disable */
+ reg = PAU_MISC_FENCE_1_CONFIG;
+ val = pau_read(pau, reg);
+ val &= ~PAU_FIR1_NDL_BRICKS_0_5;
+ val &= ~PAU_FIR1_NDL_BRICKS_6_11;
+ pau_write(pau, reg, val);
+
+ /* irq disable */
+ reg = PAU_MISC_INT_1_CONFIG;
+ val = pau_read(pau, reg);
+ val &= ~PAU_FIR1_NDL_BRICKS_0_5;
+ val &= ~PAU_FIR1_NDL_BRICKS_6_11;
+ pau_write(pau, reg, val);
+}
+
static void pau_opencapi_enable_ref_clock(struct pau_dev *dev)
{
uint64_t reg, val;
@@ -817,6 +969,7 @@ static void pau_opencapi_init_hw(struct pau *pau)
pau_opencapi_mask_firs(pau);
pau_opencapi_assign_bars(pau);
+ pau_opencapi_setup_irqs(pau);
/* Create phb */
pau_for_each_opencapi_dev(dev, pau) {
@@ -893,6 +1046,10 @@ static void pau_opencapi_init_hw(struct pau *pau)
* AFU's memory
*/
+ /* Procedure 17.1.3.11 - Interrupt Configuration */
+ /* done in pau_opencapi_setup_irqs() */
+ pau_opencapi_enable_interrupt_on_error(dev);
+
/* Reset disabled. Place OTLs into Run State */
pau_opencapi_set_fence_control(dev, 0b00);
diff --git a/include/pau-regs.h b/include/pau-regs.h
index e4ff7cc..d98f435 100644
--- a/include/pau-regs.h
+++ b/include/pau-regs.h
@@ -139,6 +139,18 @@
#define PAU_MISC_HOLD (PAU_BLOCK_PAU_MISC + 0x020)
#define PAU_MISC_HOLD_NDL_STALL PPC_BITMASK(0, 3)
#define PAU_MISC_CONFIG (PAU_BLOCK_PAU_MISC + 0x030)
+#define PAU_MISC_CONFIG_IPI_PS PPC_BIT(11)
+#define PAU_MISC_CONFIG_IPI_PS_64K 1
+#define PAU_MISC_CONFIG_IPI_OS PPC_BIT(12)
+#define PAU_MISC_CONFIG_IPI_OS_AIX 0
#define PAU_MISC_CONFIG_OC_MODE PPC_BIT(16)
+#define PAU_MISC_FREEZE_1_CONFIG (PAU_BLOCK_PAU_MISC + 0x048)
+#define PAU_MISC_FENCE_1_CONFIG (PAU_BLOCK_PAU_MISC + 0x058)
+#define PAU_MISC_INT_1_CONFIG (PAU_BLOCK_PAU_MISC + 0x068)
+#define PAU_MISC_INT_BAR (PAU_BLOCK_PAU_MISC + 0x098)
+#define PAU_MISC_INT_BAR_ADDR PPC_BITMASK(0, 39)
+#define PAU_MISC_INT_2_CONFIG (PAU_BLOCK_PAU_MISC + 0x408)
+#define PAU_MISC_INT_2_CONFIG_XFAULT_2_5(n) PPC_BIT(0 + (n))
+#define PAU_MISC_INT_2_CONFIG_XFAULT_0_1(n) PPC_BIT(54 + (n))
#endif /* __PAU_REGS_H */
diff --git a/include/pau.h b/include/pau.h
index 0246a63..b6fabe7 100644
--- a/include/pau.h
+++ b/include/pau.h
@@ -33,6 +33,7 @@ struct pau_dev {
uint32_t index;
struct dt_node *dn;
struct phb phb;
+ uint32_t status;
struct pau_bar ntl_bar;
struct pau_bar genid_bar;
@@ -59,6 +60,7 @@ struct pau {
uint64_t regs[2];
bool mmio_access;
+ uint32_t irq_base;
struct lock lock;
uint32_t links;