aboutsummaryrefslogtreecommitdiff
path: root/hw/npu.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/npu.c')
-rw-r--r--hw/npu.c202
1 files changed, 131 insertions, 71 deletions
diff --git a/hw/npu.c b/hw/npu.c
index a61dd20..a0da887 100644
--- a/hw/npu.c
+++ b/hw/npu.c
@@ -18,6 +18,7 @@
#include <timebase.h>
#include <pci.h>
#include <pci-cfg.h>
+#include <pci-slot.h>
#include <interrupts.h>
#include <opal.h>
#include <opal-api.h>
@@ -28,7 +29,6 @@
#include <affinity.h>
#include <npu-regs.h>
#include <npu.h>
-#include <lock.h>
#include <xscom.h>
/*
@@ -197,20 +197,6 @@ static uint64_t get_bar_size(uint64_t bar)
return (1 << GETFIELD(NX_MMIO_BAR_SIZE, bar)) * 0x10000;
}
-static void npu_lock(struct phb *phb)
-{
- struct npu *p = phb_to_npu(phb);
-
- lock(&p->lock);
-}
-
-static void npu_unlock(struct phb *phb)
-{
- struct npu *p = phb_to_npu(phb);
-
- unlock(&p->lock);
-}
-
/* Update the changes of the device BAR to link BARs */
static void npu_dev_bar_update(uint32_t gcid, struct npu_dev_bar *bar,
bool enable)
@@ -556,7 +542,7 @@ static void npu_dev_bind_pci_dev(struct npu_dev *dev)
if (!phb)
continue;
- dev->pd = pci_walk_dev(phb, __npu_dev_bind_pci_dev, dev);
+ dev->pd = pci_walk_dev(phb, NULL, __npu_dev_bind_pci_dev, dev);
if (dev->pd) {
dev->phb = phb;
/* Found the device, set the bit in config space */
@@ -566,6 +552,11 @@ static void npu_dev_bind_pci_dev(struct npu_dev *dev)
}
}
+ /**
+ * @fwts-label NPUNotBound
+ * @fwts-advice Start debugging why we didn't find the right device.
+ * End result is that NVLink will not function properly
+ */
prlog(PR_ERR, "%s: NPU device %04x:00:%02x.0 not binding to PCI device\n",
__func__, dev->npu->phb.opal_id, dev->index);
}
@@ -600,7 +591,9 @@ static void npu_append_pci_phandle(struct dt_node *dn, u32 phandle)
unlock(&pci_npu_phandle_lock);
}
-static void npu_dn_fixup(struct phb *phb, struct pci_device *pd)
+static int npu_dn_fixup(struct phb *phb,
+ struct pci_device *pd,
+ void *data __unused)
{
struct npu *p = phb_to_npu(phb);
struct npu_dev *dev;
@@ -609,7 +602,7 @@ static void npu_dn_fixup(struct phb *phb, struct pci_device *pd)
assert(dev);
if (dev->phb || dev->pd)
- return;
+ return 0;
/* Bind the emulated PCI device with the real one, which can't
* be done until the PCI devices are populated. Once the real
@@ -625,6 +618,13 @@ static void npu_dn_fixup(struct phb *phb, struct pci_device *pd)
dt_add_property_cells(pd->dn, "ibm,gpu", dev->pd->dn->phandle);
}
+
+ return 0;
+}
+
+static void npu_phb_final_fixup(struct phb *phb)
+{
+ pci_walk_dev(phb, NULL, npu_dn_fixup, NULL);
}
static void npu_ioda_init(struct npu *p)
@@ -688,19 +688,22 @@ static int npu_isn_valid(struct npu *p, uint32_t isn)
if (p->chip_id != p8_irq_to_chip(isn) || p->index != 0 ||
NPU_IRQ_NUM(isn) < NPU_LSI_IRQ_MIN ||
NPU_IRQ_NUM(isn) > NPU_LSI_IRQ_MAX) {
- NPUERR(p, "isn 0x%x not valid for this NPU\n", isn);
+ /**
+ * @fwts-label NPUisnInvalid
+ * @fwts-advice NVLink not functional
+ */
+ prlog(PR_ERR, "NPU%d: isn 0x%x not valid for this NPU\n",
+ p->phb.opal_id, isn);
return false;
}
return true;
}
-static int64_t npu_lsi_get_xive(void *data,
- uint32_t isn,
- uint16_t *server,
- uint8_t *prio)
+static int64_t npu_lsi_get_xive(struct irq_source *is, uint32_t isn,
+ uint16_t *server, uint8_t *prio)
{
- struct npu *p = data;
+ struct npu *p = is->data;
uint32_t irq = NPU_IRQ_NUM(isn);
uint64_t lxive;
@@ -719,12 +722,10 @@ static int64_t npu_lsi_get_xive(void *data,
return OPAL_SUCCESS;
}
-static int64_t npu_lsi_set_xive(void *data,
- uint32_t isn,
- uint16_t server,
- uint8_t prio)
+static int64_t npu_lsi_set_xive(struct irq_source *is, uint32_t isn,
+ uint16_t server, uint8_t prio)
{
- struct npu *p = data;
+ struct npu *p = is->data;
uint32_t irq = NPU_IRQ_NUM(isn);
uint64_t lxive;
@@ -749,9 +750,9 @@ static int64_t npu_lsi_set_xive(void *data,
return OPAL_SUCCESS;
}
-static void npu_err_interrupt(void *data, uint32_t isn)
+static void npu_err_interrupt(struct irq_source *is, uint32_t isn)
{
- struct npu *p = data;
+ struct npu *p = is->data;
uint32_t irq = NPU_IRQ_NUM(isn);
if (!npu_isn_valid(p, isn))
@@ -978,27 +979,29 @@ static int64_t npu_set_pe(struct phb *phb,
return OPAL_SUCCESS;
}
-static int64_t npu_link_state(struct phb *phb __unused)
+static int64_t npu_get_link_state(struct pci_slot *slot __unused, uint8_t *val)
{
/* As we're emulating all PCI stuff, the link bandwidth
* isn't big deal anyway.
*/
- return OPAL_SHPC_LINK_UP_x1;
+ *val = OPAL_SHPC_LINK_UP_x1;
+ return OPAL_SUCCESS;
}
-static int64_t npu_power_state(struct phb *phb __unused)
+static int64_t npu_get_power_state(struct pci_slot *slot __unused, uint8_t *val)
{
- return OPAL_SHPC_POWER_ON;
+ *val = PCI_SLOT_POWER_ON;
+ return OPAL_SUCCESS;
}
-static int64_t npu_hreset(struct phb *phb __unused)
+static int64_t npu_hreset(struct pci_slot *slot __unused)
{
prlog(PR_DEBUG, "NPU: driver should call reset procedure here\n");
return OPAL_SUCCESS;
}
-static int64_t npu_freset(struct phb *phb __unused)
+static int64_t npu_freset(struct pci_slot *slot __unused)
{
/* FIXME: PHB fundamental reset, which need to be
* figured out later. It's used by EEH recovery
@@ -1007,6 +1010,33 @@ static int64_t npu_freset(struct phb *phb __unused)
return OPAL_SUCCESS;
}
+static struct pci_slot *npu_slot_create(struct phb *phb)
+{
+ struct pci_slot *slot;
+
+ slot = pci_slot_alloc(phb, NULL);
+ if (!slot)
+ return slot;
+
+ /* Elementary functions */
+ slot->ops.get_presence_state = NULL;
+ slot->ops.get_link_state = npu_get_link_state;
+ slot->ops.get_power_state = npu_get_power_state;
+ slot->ops.get_attention_state = NULL;
+ slot->ops.get_latch_state = NULL;
+ slot->ops.set_power_state = NULL;
+ slot->ops.set_attention_state = NULL;
+
+ slot->ops.prepare_link_change = NULL;
+ slot->ops.poll_link = NULL;
+ slot->ops.hreset = npu_hreset;
+ slot->ops.freset = npu_freset;
+ slot->ops.pfreset = NULL;
+ slot->ops.creset = NULL;
+
+ return slot;
+}
+
static int64_t npu_freeze_status(struct phb *phb,
uint64_t pe_number __unused,
uint8_t *freeze_state,
@@ -1059,6 +1089,17 @@ static int64_t npu_eeh_next_error(struct phb *phb,
return OPAL_SUCCESS;
}
+/* For use in error injection and handling. */
+void npu_set_fence_state(struct npu *p, bool fence) {
+ p->fenced = fence;
+
+ if (fence)
+ prlog(PR_ERR, "NPU: Chip %x is fenced, reboot required.\n",
+ p->chip_id);
+ else
+ prlog(PR_WARNING, "NPU: un-fencing is dangerous and should \
+ only be used for development purposes.");
+}
/* Sets the NPU to trigger an error when a DMA occurs */
static int64_t npu_err_inject(struct phb *phb, uint32_t pe_num,
@@ -1092,9 +1133,12 @@ static int64_t npu_err_inject(struct phb *phb, uint32_t pe_num,
return OPAL_PARAMETER;
} else if (type == 1) {
/* Emulate fence mode. */
- p->fenced = true;
+ npu_set_fence_state(p, true);
} else {
- /* Cause a freeze with an invalid MMIO write. */
+ /* Cause a freeze with an invalid MMIO read. If the BAR is not
+ * enabled, this will checkstop the machine.
+ */
+ npu_dev_bar_update(p->chip_id, &dev->bar, true);
in_be64((void *)dev->bar.base);
}
@@ -1102,8 +1146,6 @@ static int64_t npu_err_inject(struct phb *phb, uint32_t pe_num,
}
static const struct phb_ops npu_ops = {
- .lock = npu_lock,
- .unlock = npu_unlock,
.cfg_read8 = npu_dev_cfg_read8,
.cfg_read16 = npu_dev_cfg_read16,
.cfg_read32 = npu_dev_cfg_read32,
@@ -1111,9 +1153,9 @@ static const struct phb_ops npu_ops = {
.cfg_write16 = npu_dev_cfg_write16,
.cfg_write32 = npu_dev_cfg_write32,
.choose_bus = NULL,
+ .get_reserved_pe_number = NULL,
.device_init = NULL,
- .device_node_fixup = npu_dn_fixup,
- .presence_detect = NULL,
+ .phb_final_fixup = npu_phb_final_fixup,
.ioda_reset = npu_ioda_reset,
.papr_errinjct_reset = NULL,
.pci_reinit = NULL,
@@ -1128,14 +1170,6 @@ static const struct phb_ops npu_ops = {
.get_msi_64 = NULL,
.set_pe = npu_set_pe,
.set_peltv = NULL,
- .link_state = npu_link_state,
- .power_state = npu_power_state,
- .slot_power_off = NULL,
- .slot_power_on = NULL,
- .hot_reset = npu_hreset,
- .fundamental_reset = npu_freset,
- .complete_reset = NULL,
- .poll = NULL,
.eeh_freeze_status = npu_freeze_status,
.eeh_freeze_clear = NULL,
.eeh_freeze_set = NULL,
@@ -1262,6 +1296,10 @@ static void npu_probe_phb(struct dt_node *dn)
xscom_read(gcid, npu_link_scom_base(dn, xscom, 1) + NX_MMIO_BAR_1,
&val);
if (!(val & NX_MMIO_BAR_ENABLE)) {
+ /**
+ * @fwts-label NPUATBARDisabled
+ * @fwts-advice NVLink not functional
+ */
prlog(PR_ERR, " AT BAR disabled!\n");
return;
}
@@ -1274,6 +1312,12 @@ static void npu_probe_phb(struct dt_node *dn)
/* Create PCI root device node */
np = dt_new_addr(dt_root, "pciex", at_bar[0]);
if (!np) {
+ /**
+ * @fwts-label NPUPHBDeviceNodeFailure
+ * @fwts-advice Error adding the PHB device node. The
+ * only real reason for this is that firmware may have
+ * run out of memory.
+ */
prlog(PR_ERR, "%s: Cannot create PHB device node\n",
__func__);
return;
@@ -1496,7 +1540,7 @@ static void npu_dev_create_cfg(struct npu_dev *dev)
/* 0x10 - BARs, always 64-bits non-prefetchable
*
* Each emulated device represents one link and therefore
- * there is one BAR for the assocaited DLTL region.
+ * there is one BAR for the associated DLTL region.
*/
/* Low 32-bits */
@@ -1691,11 +1735,19 @@ static void npu_add_phb_properties(struct npu *p)
uint32_t icsp = get_ics_phandle();
uint64_t tkill, mm_base, mm_size;
uint32_t base_lsi = p->base_lsi;
- uint32_t map[] = { 0x0, 0x0, 0x0, 0x1, icsp, base_lsi,
- 0x0, 0x0, 0x0, 0x2, icsp, base_lsi + 1,
- 0x800, 0x0, 0x0, 0x1, icsp, base_lsi + 2,
- 0x800, 0x0, 0x0, 0x2, icsp, base_lsi + 3 };
+ uint32_t map[] = {
+ /* Dev 0 INT#A (used by fn0) */
+ 0x0000, 0x0, 0x0, 0x1, icsp, base_lsi + NPU_LSI_INT_DL0, 1,
+ /* Dev 0 INT#B (used by fn1) */
+ 0x0000, 0x0, 0x0, 0x2, icsp, base_lsi + NPU_LSI_INT_DL1, 1,
+ /* Dev 1 INT#A (used by fn0) */
+ 0x0800, 0x0, 0x0, 0x1, icsp, base_lsi + NPU_LSI_INT_DL2, 1,
+ /* Dev 1 INT#B (used by fn1) */
+ 0x0800, 0x0, 0x0, 0x2, icsp, base_lsi + NPU_LSI_INT_DL3, 1,
+ };
+ /* Mask is bus, device and INT# */
uint32_t mask[] = {0xf800, 0x0, 0x0, 0x7};
+ char slotbuf[32];
/* Add various properties that HB doesn't have to
* add, some of them simply because they result from
@@ -1710,21 +1762,8 @@ static void npu_add_phb_properties(struct npu *p)
dt_add_property_cells(np, "clock-frequency", 0x200, 0);
dt_add_property_cells(np, "interrupt-parent", icsp);
- /* DLPL Interrupts */
- p->phb.lstate.int_size = 1;
- p->phb.lstate.int_val[0][0] = p->base_lsi + NPU_LSI_INT_DL0;
- p->phb.lstate.int_val[1][0] = p->base_lsi + NPU_LSI_INT_DL1;
- p->phb.lstate.int_val[2][0] = p->base_lsi + NPU_LSI_INT_DL2;
- p->phb.lstate.int_val[3][0] = p->base_lsi + NPU_LSI_INT_DL3;
- p->phb.lstate.int_parent[0] = icsp;
- p->phb.lstate.int_parent[1] = icsp;
- p->phb.lstate.int_parent[2] = icsp;
- p->phb.lstate.int_parent[3] = icsp;
-
- /* Due to the way the emulated PCI devices are structured in
- * the device tree the core PCI layer doesn't do this for
- * us. Besides the swizzling wouldn't suit our needs even if it
- * did. */
+ /* DLPL Interrupts, we don't use the standard swizzle */
+ p->phb.lstate.int_size = 0;
dt_add_property(np, "interrupt-map", map, sizeof(map));
dt_add_property(np, "interrupt-map-mask", mask, sizeof(mask));
@@ -1750,12 +1789,20 @@ static void npu_add_phb_properties(struct npu *p)
hi32(mm_base), lo32(mm_base),
hi32(mm_base), lo32(mm_base),
hi32(mm_size), lo32(mm_size));
+
+ /* Set the slot location on the NPU PHB. This PHB can contain
+ * devices that correlate with multiple physical slots, so
+ * present the chip ID instead.
+ */
+ snprintf(slotbuf, sizeof(slotbuf), "NPU Chip %d", p->chip_id);
+ dt_add_property_string(np, "ibm,io-base-loc-code", slotbuf);
}
static void npu_create_phb(struct dt_node *dn)
{
const struct dt_property *prop;
struct npu *p;
+ struct pci_slot *slot;
uint32_t links;
void *pmem;
@@ -1800,6 +1847,18 @@ static void npu_create_phb(struct dt_node *dn)
/* Populate extra properties */
npu_add_phb_properties(p);
+ /* Create PHB slot */
+ slot = npu_slot_create(&p->phb);
+ if (!slot)
+ {
+ /**
+ * @fwts-label NPUCannotCreatePHBSlot
+ * @fwts-advice Firmware probably ran out of memory creating
+ * NPU slot. NVLink functionality could be broken.
+ */
+ prlog(PR_ERR, "NPU: Cannot create PHB slot\n");
+ }
+
/* Register PHB */
pci_register_phb(&p->phb, OPAL_DYNAMIC_PHB_ID);
@@ -1825,3 +1884,4 @@ void probe_npu(void)
dt_for_each_compatible(dt_root, np, "ibm,power8-npu-pciex")
npu_create_phb(np);
}
+