/* Copyright 2013-2015 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.
 */
#include <skiboot.h>
#include <io.h>
#include <timebase.h>
#include <pci.h>
#include <pci-cfg.h>
#include <pci-virt.h>
#include <pci-slot.h>
#include <interrupts.h>
#include <opal.h>
#include <opal-api.h>
#include <cpu.h>
#include <device.h>
#include <ccan/str/str.h>
#include <ccan/array_size/array_size.h>
#include <ccan/build_assert/build_assert.h>
#include <affinity.h>
#include <npu-regs.h>
#include <npu.h>
#include <xscom.h>
#include <string.h>

/*
 * Terminology:
 *
 *  Brick - A group of either 8 TX or 8 RX lanes
 *  Link - A group of 8 TX and 8 RX lanes
 *
 * Each link is represented in system software as an emulated PCI
 * device. Garrison has two chips each with 4 links, therefore there
 * are 8 emulated PCI devices in total.
 *
 *  +----------------------------------------------------------------+
 *  |              PBCQ3 (SCOM Base Address 0x2012c00)               |
 *  |               PHB3 (SCOM Base Address 0x9012c00)               |
 *  +----------------------------------------------------------------+
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *  +----------------------------------------------------------------+
 *  |                             PCIe x8                            |
 *  +----------------------------------------------------------------+
 *  |                               GPU0                             |
 *  +--------------------------------+-------------------------------+
 *  |           NV Link 1            |           NV Link 0           |
 *  +---------------+----------------+---------------+---------------+
 *  |      RX       |      TX        |      RX       |      TX       |
 *  +---------------+----------------+---------------+---------------+
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *  +---------------+----------------+---------------+---------------+
 *  |      TX       |      RX        |      TX       |      RX       |
 *  +---------------+----------------+---------------+---------------+
 *  |           Lanes [0:7]         PHY 0       Lanes [8:15]         |
 *  |               SCOM Base Address 0x8000080008010c3f             |
 *  +--------------------------------+-------------------------------+
 *  |          Link 0 NDL/NTL        |         Link 1 NTL/NDL        |
 *  |   SCOM Base Address 0x8013c00  |  SCOM Base Address 0x8013c40  |
 *  +--------------------------------+-------------------------------+
 *  |                                                                |
 *  |          Address Translation/AT (shared for all links)         |
 *  |                 SCOM Base Address 0x8013d80                    |
 *  |                                                                |
 *  +--------------------------------+-------------------------------+
 *  |          Link 3 NDL/NTL        |         Link 4 NTL/NDL        |
 *  |   SCOM Base Address 0x8013d00  |  SCOM Base Address 0x8013d40  |
 *  +--------------------------------+-------------------------------+
 *  |           Lanes [8:15]        PHY 1       Lanes [0:7]          |
 *  |               SCOM Base Address 0x8000080008010c7f             |
 *  +---------------+----------------+---------------+---------------+
 *  |      TX       |      RX        |      TX       |      RX       |
 *  +---------------+----------------+---------------+---------------+
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *      ||||||||        ||||||||         ||||||||        ||||||||
 *  +---------------+----------------+---------------+---------------+
 *  |      RX       |      TX        |      RX       |      TX       |
 *  +---------------+----------------+---------------+---------------+
 *  |           NV Link 2            |           NV Link 3           |
 *  +--------------------------------+-------------------------------+
 *  |                               GPU1                             |
 *  +----------------------------------------------------------------+
 *  |                             PCIe x8                            |
 *  +----------------------------------------------------------------+
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *                          ||||||||  ||||||||
 *  +----------------------------------------------------------------+
 *  |               PHB2 (SCOM Base Address 0x9012800)               |
 *  |              PBCQ2 (SCOM Base Address 0x2012800)               |
 *  +----------------------------------------------------------------+
 *
 */

static struct npu_dev_cap *npu_dev_find_capability(struct npu_dev *dev,
						   uint16_t id);

#define OPAL_NPU_VERSION          0x02

#define PCIE_CAP_START	          0x40
#define PCIE_CAP_END	          0x80
#define VENDOR_CAP_START          0x80
#define VENDOR_CAP_END	          0x90

#define VENDOR_CAP_PCI_DEV_OFFSET 0x0d

/* Returns the scom base for the given link index */
static uint64_t npu_link_scom_base(struct dt_node *dn, uint32_t scom_base,
				   int index)
{
	struct dt_node *link;
	uint32_t link_index;
	char namebuf[32];

	snprintf(namebuf, sizeof(namebuf), "link@%x", index);
	link = dt_find_by_name(dn, namebuf);
	assert(link);
	link_index = dt_prop_get_u32(link, "ibm,npu-link-index");
	return scom_base + (link_index * NPU_LINK_SIZE);
}

static uint64_t get_bar_size(uint64_t bar)
{
	return (1 << GETFIELD(NX_MMIO_BAR_SIZE, bar)) * 0x10000;
}

/* 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)
{
	uint64_t val;

	if (!bar->xscom)
		return;

	val = bar->base;
	val = SETFIELD(NX_MMIO_BAR_SIZE, val, ilog2(bar->size / 0x10000));
	if (enable)
		val |= NX_MMIO_BAR_ENABLE;
	xscom_write(gcid, bar->xscom, val);
}

/* Trap for PCI command (0x4) to enable or disable device's BARs */
static int64_t npu_dev_cfg_write_cmd(void *dev,
				     struct pci_cfg_reg_filter *pcrf __unused,
				     uint32_t offset, uint32_t size,
				     uint32_t *data, bool write)
{
	struct pci_virt_device *pvd = dev;
	struct npu_dev *ndev = pvd->data;
	bool enable;

	if (!write)
		return OPAL_PARTIAL;

	if (offset != PCI_CFG_CMD)
		return OPAL_PARAMETER;
	if (size != 1 && size != 2 && size != 4)
		return OPAL_PARAMETER;

	/* Update device BARs and link BARs will be syncrhonized
	 * with hardware automatically.
	 */
	enable = !!(*data & PCI_CFG_CMD_MEM_EN);
	npu_dev_bar_update(ndev->npu->chip_id, &ndev->bar, enable);

	/* Normal path to update PCI config buffer */
	return OPAL_PARTIAL;
}

/*
 * Trap for memory BARs: 0xFF's should be written to BAR register
 * prior to getting its size.
 */
static int64_t npu_dev_cfg_bar_read(struct npu_dev *dev __unused,
				    struct pci_cfg_reg_filter *pcrf,
				    uint32_t offset, uint32_t size,
				    uint32_t *data)
{
	struct npu_dev_bar *bar = (struct npu_dev_bar *)(pcrf->data);

	/* Revert to normal path if we weren't trapped for BAR size */
	if (!bar->trapped)
		return OPAL_PARTIAL;

	if (offset != pcrf->start &&
	    offset != pcrf->start + 4)
		return OPAL_PARAMETER;
	if (size != 4)
		return OPAL_PARAMETER;

	bar->trapped = false;
	*data = bar->bar_sz;
	return OPAL_SUCCESS;
}

static int64_t npu_dev_cfg_bar_write(struct npu_dev *dev,
				     struct pci_cfg_reg_filter *pcrf,
				     uint32_t offset, uint32_t size,
				     uint32_t data)
{
	struct pci_virt_device *pvd = dev->pvd;
	struct npu_dev_bar *bar = (struct npu_dev_bar *)(pcrf->data);
	uint32_t pci_cmd;

	if (offset != pcrf->start &&
	    offset != pcrf->start + 4)
		return OPAL_PARAMETER;
	if (size != 4)
		return OPAL_PARAMETER;

	/* Return BAR size on next read */
	if (data == 0xffffffff) {
		bar->trapped = true;
		if (offset == pcrf->start)
			bar->bar_sz = (bar->size & 0xffffffff);
		else
			bar->bar_sz = (bar->size >> 32);

		return OPAL_SUCCESS;
	}

	/* Update BAR base address */
	if (offset == pcrf->start) {
		bar->base &= 0xffffffff00000000UL;
		bar->base |= (data & 0xfffffff0);
	} else {
		bar->base &= 0x00000000ffffffffUL;
		bar->base |= ((uint64_t)data << 32);

		PCI_VIRT_CFG_NORMAL_RD(pvd, PCI_CFG_CMD, 4, &pci_cmd);
		npu_dev_bar_update(dev->npu->chip_id, bar,
				   !!(pci_cmd & PCI_CFG_CMD_MEM_EN));
	}

	/* We still depend on the normal path to update the
	 * cached config buffer.
	 */
	return OPAL_PARAMETER;
}

static int64_t npu_dev_cfg_bar(void *dev, struct pci_cfg_reg_filter *pcrf,
			       uint32_t offset, uint32_t len, uint32_t *data,
			       bool write)
{
	struct pci_virt_device *pvd = dev;
	struct npu_dev *ndev = pvd->data;

	if (write)
		return npu_dev_cfg_bar_write(ndev, pcrf, offset, len, *data);

	return npu_dev_cfg_bar_read(ndev, pcrf, offset, len, data);
}

static int64_t npu_dev_cfg_exp_devcap(void *dev,
		struct pci_cfg_reg_filter *pcrf __unused,
		uint32_t offset, uint32_t size,
		uint32_t *data, bool write)
{
	struct pci_virt_device *pvd = dev;
	struct npu_dev *ndev = pvd->data;

	assert(write);

	if ((size != 2) || (offset & 1)) {
		/* Short config writes are not supported */
		prlog(PR_ERR, "NPU%d: Unsupported write to pcie control register\n",
		      ndev->phb->opal_id);
		return OPAL_PARAMETER;
	}

	if (*data & PCICAP_EXP_DEVCTL_FUNC_RESET)
		npu_dev_procedure_reset(ndev);

	return OPAL_PARTIAL;
}

static struct npu_dev *bdfn_to_npu_dev(struct npu *p, uint32_t bdfn)
{
	struct pci_virt_device *pvd;

	/* Sanity check */
	if (bdfn & ~0xff)
		return NULL;

	pvd = pci_virt_find_device(&p->phb, bdfn);
	if (pvd)
		return pvd->data;

	return NULL;
}

#define NPU_CFG_READ(size, type)						\
static int64_t npu_cfg_read##size(struct phb *phb, uint32_t bdfn,		\
				  uint32_t offset, type *data)			\
{										\
	uint32_t val;								\
	int64_t ret;								\
										\
	ret = pci_virt_cfg_read(phb, bdfn, offset, sizeof(*data), &val);	\
	*data = (type)val;							\
	return ret;								\
}
#define NPU_CFG_WRITE(size, type)						\
static int64_t npu_cfg_write##size(struct phb *phb, uint32_t bdfn,		\
				   uint32_t offset, type data)			\
{										\
	uint32_t val = data;                                            	\
										\
	return pci_virt_cfg_write(phb, bdfn, offset, sizeof(data), val);	\
}

NPU_CFG_READ(8,   u8);
NPU_CFG_READ(16,  u16);
NPU_CFG_READ(32,  u32);
NPU_CFG_WRITE(8,  u8);
NPU_CFG_WRITE(16, u16);
NPU_CFG_WRITE(32, u32);

static int __npu_dev_bind_pci_dev(struct phb *phb __unused,
				  struct pci_device *pd,
				  void *data)
{
	struct npu_dev *dev = data;
	struct dt_node *pci_dt_node;
	char *pcislot;

	/* Ignore non-nvidia PCI devices */
	if ((pd->vdid & 0xffff) != 0x10de)
		return 0;

	/* Find the PCI device's slot location */
	for (pci_dt_node = pd->dn;
	     pci_dt_node && !dt_find_property(pci_dt_node, "ibm,slot-label");
	     pci_dt_node = pci_dt_node->parent);

	if (!pci_dt_node)
		return 0;

	pcislot = (char *)dt_prop_get(pci_dt_node, "ibm,slot-label");

	prlog(PR_DEBUG, "NPU: comparing GPU %s and NPU %s\n",
	      pcislot, dev->slot_label);

	if (streq(pcislot, dev->slot_label))
		return 1;

	return 0;
}

static void npu_dev_bind_pci_dev(struct npu_dev *dev)
{
	struct phb *phb;
	uint32_t i;

	if (dev->pd)
		return;

	for (i = 0; i < 64; i++) {
		if (dev->npu->phb.opal_id == i)
			continue;

		phb = pci_get_phb(i);
		if (!phb)
			continue;

		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 */
			PCI_VIRT_CFG_INIT_RO(dev->pvd, VENDOR_CAP_START +
				VENDOR_CAP_PCI_DEV_OFFSET, 1, 0x01);
			return;
		}
	}

	prlog(PR_INFO, "%s: No PCI device for NPU device %04x:00:%02x.0 to bind to. If you expect a GPU to be there, this is a problem.\n",
	      __func__, dev->npu->phb.opal_id, dev->index);
}

static struct lock pci_npu_phandle_lock = LOCK_UNLOCKED;

/* Appends an NPU phandle to the given PCI device node ibm,npu
 * property */
static void npu_append_pci_phandle(struct dt_node *dn, u32 phandle)
{
	uint32_t *npu_phandles;
	struct dt_property *pci_npu_phandle_prop;
	size_t prop_len;

	/* Use a lock to make sure no one else has a reference to an
	 * ibm,npu property (this assumes this is the only function
	 * that holds a reference to it). */
	lock(&pci_npu_phandle_lock);

	/* This function shouldn't be called unless ibm,npu exists */
	pci_npu_phandle_prop = (struct dt_property *)
		dt_require_property(dn, "ibm,npu", -1);

	/* Need to append to the properties */
	prop_len = pci_npu_phandle_prop->len;
	prop_len += sizeof(*npu_phandles);
	dt_resize_property(&pci_npu_phandle_prop, prop_len);
	pci_npu_phandle_prop->len = prop_len;

	npu_phandles = (uint32_t *) pci_npu_phandle_prop->prop;
	npu_phandles[prop_len/sizeof(*npu_phandles) - 1] = phandle;
	unlock(&pci_npu_phandle_lock);
}

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;

	dev = bdfn_to_npu_dev(p, pd->bdfn);
	assert(dev);

	if (dev->phb || dev->pd)
		return 0;

	/* NPU devices require a slot location to associate with GPUs */
	dev->slot_label = dt_prop_get(pd->dn, "ibm,slot-label");

	/* Bind the emulated PCI device with the real one, which can't
	 * be done until the PCI devices are populated. Once the real
	 * PCI device is identified, we also need fix the device-tree
	 * for it
	 */
	npu_dev_bind_pci_dev(dev);
	if (dev->phb && dev->pd && dev->pd->dn) {
		if (dt_find_property(dev->pd->dn, "ibm,npu"))
			npu_append_pci_phandle(dev->pd->dn, pd->dn->phandle);
		else
			dt_add_property_cells(dev->pd->dn, "ibm,npu", pd->dn->phandle);

		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)
{
	uint64_t *data64;
	uint32_t i;

	/* LXIVT - Disable all LSIs */
	for (i = 0; i < ARRAY_SIZE(p->lxive_cache); i++) {
		data64 = &p->lxive_cache[i];
		*data64 = SETFIELD(NPU_IODA_LXIVT_PRIORITY, 0ul, 0xff);
		*data64 = SETFIELD(NPU_IODA_LXIVT_SERVER, *data64, 0);
	}

	/* PCT - Reset to reserved PE# */
	for (i = 0; i < ARRAY_SIZE(p->pce_cache); i++) {
		data64 = &p->pce_cache[i];
		*data64 = SETFIELD(NPU_IODA_PCT_PE, 0ul, 0ul);
		*data64 |= NPU_IODA_PCT_LINK_ENABLED;
	}

	/* Clear TVT */
	memset(p->tve_cache, 0, sizeof(p->tve_cache));
}

static int64_t npu_ioda_reset(struct phb *phb, bool purge)
{
	struct npu *p = phb_to_npu(phb);
	uint32_t i;

	if (purge) {
		NPUDBG(p, "Purging all IODA tables...\n");
		npu_ioda_init(p);
	}

	/* LIST */
	npu_ioda_sel(p, NPU_IODA_TBL_LIST, 0, true);
	for (i = 0; i < 8; i++)
		out_be64(p->at_regs + NPU_IODA_DATA0, 0x1);

	/* LIXVT */
	npu_ioda_sel(p, NPU_IODA_TBL_LXIVT, 0, true);
	for (i = 0; i < ARRAY_SIZE(p->lxive_cache); i++)
		out_be64(p->at_regs + NPU_IODA_DATA0, p->lxive_cache[i]);

	/* PCT */
	npu_ioda_sel(p, NPU_IODA_TBL_PCT, 0, true);
	for (i = 0; i < ARRAY_SIZE(p->pce_cache); i++)
		out_be64(p->at_regs + NPU_IODA_DATA0, p->pce_cache[i]);

	/* TVT */
	npu_ioda_sel(p, NPU_IODA_TBL_TVT, 0, true);
	for (i = 0; i < ARRAY_SIZE(p->tve_cache); i++)
		out_be64(p->at_regs + NPU_IODA_DATA0, p->tve_cache[i]);

	return OPAL_SUCCESS;
}

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) {
		/**
		 * @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(struct irq_source *is, uint32_t isn,
				uint16_t *server, uint8_t *prio)
{
	struct npu *p = is->data;
	uint32_t irq = NPU_IRQ_NUM(isn);
	uint64_t lxive;

	if (!npu_isn_valid(p, isn))
		return OPAL_PARAMETER;

	/* The content is fetched from the cache, which requires
	 * that the initial cache should be initialized with the
	 * default values
	 */
	irq -= NPU_LSI_IRQ_MIN;
	lxive = p->lxive_cache[irq];
	*server = GETFIELD(NPU_IODA_LXIVT_SERVER, lxive);
	*prio = GETFIELD(NPU_IODA_LXIVT_PRIORITY, lxive);

	return OPAL_SUCCESS;
}

static int64_t npu_lsi_set_xive(struct irq_source *is, uint32_t isn,
				uint16_t server, uint8_t prio)
{
	struct npu *p = is->data;
	uint32_t irq = NPU_IRQ_NUM(isn);
	uint64_t lxive;

	if (!npu_isn_valid(p, isn))
		return OPAL_PARAMETER;

	/* Figure out LXIVT entry */
	lxive = SETFIELD(NPU_IODA_LXIVT_SERVER, 0ul, server);
	lxive = SETFIELD(NPU_IODA_LXIVT_PRIORITY, lxive, prio);

	/* Cache LXIVT entry */
	irq -= NPU_LSI_IRQ_MIN;
	p->lxive_cache[irq] = lxive;

	/* Update to LXIVT entry */
	npu_ioda_sel(p, NPU_IODA_TBL_LXIVT, irq, false);
	lxive = in_be64(p->at_regs + NPU_IODA_DATA0);
	lxive = SETFIELD(NPU_IODA_LXIVT_SERVER, lxive, server);
	lxive = SETFIELD(NPU_IODA_LXIVT_PRIORITY, lxive, prio);
	out_be64(p->at_regs + NPU_IODA_DATA0, lxive);

	return OPAL_SUCCESS;
}

static void npu_err_interrupt(struct irq_source *is, uint32_t isn)
{
	struct npu *p = is->data;
	uint32_t irq = NPU_IRQ_NUM(isn);

	if (!npu_isn_valid(p, isn))
		return;

	/* There're 4 LSIs used for error reporting: 4/5 for data
	 * link error reporting while 6/7 for frozen PE detection
	 */
	irq -= NPU_LSI_IRQ_MIN;
	switch (irq) {
	case 4 ... 5:
		prerror("Invalid NPU error interrupt received\n");
		break;
	case 6 ... 7:
		opal_update_pending_evt(OPAL_EVENT_PCI_ERROR,
					OPAL_EVENT_PCI_ERROR);
	}
}

static uint64_t npu_lsi_attributes(struct irq_source *is, uint32_t isn)
{
	struct npu *p = is->data;
	uint32_t idx = isn - p->base_lsi;

	if (idx >= 4)
		return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TARGET_RARE | IRQ_ATTR_TYPE_LSI;
	return IRQ_ATTR_TARGET_LINUX;
}

/* Error LSIs (skiboot owned) */
static const struct irq_source_ops npu_lsi_irq_ops = {
	.get_xive	= npu_lsi_get_xive,
	.set_xive	= npu_lsi_set_xive,
	.attributes	= npu_lsi_attributes,
	.interrupt	= npu_err_interrupt,
};

static void npu_register_irq(struct npu *p)
{
	register_irq_source(&npu_lsi_irq_ops, p, p->base_lsi, 8);
}

static void npu_hw_init(struct npu *p)
{
	/* 3 MMIO setup for AT */
	out_be64(p->at_regs + NPU_LSI_SOURCE_ID,
		 SETFIELD(NPU_LSI_SRC_ID_BASE, 0ul, NPU_LSI_IRQ_MIN >> 4));
	BUILD_ASSERT((NPU_LSI_IRQ_MIN & 0x07F0) == NPU_LSI_IRQ_MIN);
	out_be64(p->at_regs + NPU_INTREP_TIMER, 0x0ul);
	npu_ioda_reset(&p->phb, false);
}

static int64_t npu_map_pe_dma_window_real(struct phb *phb,
					   uint64_t pe_number,
					   uint16_t window_id,
					   uint64_t pci_start_addr,
					   uint64_t pci_mem_size)
{
	struct npu *p = phb_to_npu(phb);
	uint64_t end;
	uint64_t tve;

	/* Sanity check. Each PE has one corresponding TVE */
	if (pe_number >= NPU_NUM_OF_PES ||
	    window_id != pe_number)
		return OPAL_PARAMETER;

	if (pci_mem_size) {
		/* Enable */

		end = pci_start_addr + pci_mem_size;

		/* We have to be 16M aligned */
		if ((pci_start_addr & 0x00ffffff) ||
		    (pci_mem_size & 0x00ffffff))
			return OPAL_PARAMETER;

		/*
		 * It *looks* like this is the max we can support (we need
		 * to verify this. Also we are not checking for rollover,
		 * but then we aren't trying too hard to protect ourselves
		 * againt a completely broken OS.
		 */
		if (end > 0x0003ffffffffffffull)
			return OPAL_PARAMETER;

		/*
		 * Put start address bits 49:24 into TVE[52:53]||[0:23]
		 * and end address bits 49:24 into TVE[54:55]||[24:47]
		 * and set TVE[51]
		 */
		tve  = (pci_start_addr << 16) & (0xffffffull << 48);
		tve |= (pci_start_addr >> 38) & (3ull << 10);
		tve |= (end >>  8) & (0xfffffful << 16);
		tve |= (end >> 40) & (3ull << 8);
		tve |= PPC_BIT(51);
	} else {
		/* Disable */
		tve = 0;
	}

	npu_ioda_sel(p, NPU_IODA_TBL_TVT, window_id, false);
	out_be64(p->at_regs + NPU_IODA_DATA0, tve);
	p->tve_cache[window_id] = tve;

	return OPAL_SUCCESS;
}

static int64_t npu_map_pe_dma_window(struct phb *phb,
					 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 npu *p = phb_to_npu(phb);
	uint64_t tts_encoded;
	uint64_t data64 = 0;

	/* Sanity check. Each PE has one corresponding TVE */
	if (pe_number >= NPU_NUM_OF_PES ||
	    window_id != pe_number)
		return OPAL_PARAMETER;

	/* Special condition, zero TCE table size used to disable
	 * the TVE.
	 */
	if (!tce_table_size) {
		npu_ioda_sel(p, NPU_IODA_TBL_TVT, window_id, false);
		out_be64(p->at_regs + NPU_IODA_DATA0, 0ul);
		p->tve_cache[window_id] = 0ul;
		return OPAL_SUCCESS;
	}

	/* Additional arguments validation */
	if (tce_levels < 1 ||
	    tce_levels > 4 ||
	    !is_pow2(tce_table_size) ||
	    tce_table_size < 0x1000)
		return OPAL_PARAMETER;

	/* TCE table size */
	data64 = SETFIELD(NPU_IODA_TVT_TTA, 0ul, tce_table_addr >> 12);
	tts_encoded = ilog2(tce_table_size) - 11;
	if (tts_encoded > 39)
		return OPAL_PARAMETER;
	data64 = SETFIELD(NPU_IODA_TVT_SIZE, data64, tts_encoded);

	/* TCE page size */
	switch (tce_page_size) {
	case 0x10000:		/* 64K */
		data64 = SETFIELD(NPU_IODA_TVT_PSIZE, data64, 5);
		break;
	case 0x1000000:		/* 16M */
		data64 = SETFIELD(NPU_IODA_TVT_PSIZE, data64, 13);
		break;
	case 0x10000000:	/* 256M */
		data64 = SETFIELD(NPU_IODA_TVT_PSIZE, data64, 17);
		break;
	case 0x1000:		/* 4K */
	default:
		data64 = SETFIELD(NPU_IODA_TVT_PSIZE, data64, 1);
	}

	/* Number of levels */
	data64 = SETFIELD(NPU_IODA_TVT_LEVELS, data64, tce_levels - 1);

	/* Update to hardware */
	npu_ioda_sel(p, NPU_IODA_TBL_TVT, window_id, false);
	out_be64(p->at_regs + NPU_IODA_DATA0, data64);
	p->tve_cache[window_id] = data64;

	return OPAL_SUCCESS;
}

static int64_t npu_set_pe(struct phb *phb,
			      uint64_t pe_number,
			      uint64_t bdfn,
			      uint8_t bcompare,
			      uint8_t dcompare,
			      uint8_t fcompare,
			      uint8_t action)
{
	struct npu *p = phb_to_npu(phb);
	struct npu_dev *dev;
	uint32_t link_idx;
	uint64_t *data64;

	/* Sanity check */
	if (action != OPAL_MAP_PE &&
	    action != OPAL_UNMAP_PE)
		return OPAL_PARAMETER;
	if (pe_number >= NPU_NUM_OF_PES)
		return OPAL_PARAMETER;

	/* All emulated PCI devices hooked to root bus, whose
	 * bus number is zero.
	 */
	dev = bdfn_to_npu_dev(p, bdfn);
	if ((bdfn >> 8) || !dev)
		return OPAL_PARAMETER;

	link_idx = dev->index;
	dev->pe_number = pe_number;

	/* Separate links will be mapped to different PEs */
	if (bcompare != OpalPciBusAll ||
	    dcompare != OPAL_COMPARE_RID_DEVICE_NUMBER ||
	    fcompare != OPAL_COMPARE_RID_FUNCTION_NUMBER)
		return OPAL_UNSUPPORTED;

	/* Map the link to the corresponding PE */
	data64 = &p->pce_cache[link_idx];
	if (action == OPAL_MAP_PE)
		*data64 = SETFIELD(NPU_IODA_PCT_PE, *data64,
				   pe_number);
	else
		*data64 = SETFIELD(NPU_IODA_PCT_PE, *data64,
				   NPU_NUM_OF_PES);

	*data64 |= NPU_IODA_PCT_LINK_ENABLED;

	npu_ioda_sel(p, NPU_IODA_TBL_PCT, link_idx, false);
	out_be64(p->at_regs + NPU_IODA_DATA0, *data64);

	return OPAL_SUCCESS;
}

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.
	 */
	*val = OPAL_SHPC_LINK_UP_x1;
	return OPAL_SUCCESS;
}

static int64_t npu_get_power_state(struct pci_slot *slot __unused, uint8_t *val)
{
	*val = PCI_SLOT_POWER_ON;
	return OPAL_SUCCESS;
}

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 pci_slot *slot __unused)
{
	/* FIXME: PHB fundamental reset, which need to be
	 * figured out later. It's used by EEH recovery
	 * upon fenced AT.
	 */
	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.creset              = NULL;

	return slot;
}

static int64_t npu_freeze_status(struct phb *phb,
				     uint64_t pe_number __unused,
				     uint8_t *freeze_state,
				     uint16_t *pci_error_type __unused,
				     uint16_t *severity __unused)
{
	/* FIXME: When it's called by skiboot PCI config accessor,
	 * the PE number is fixed to 0, which is incorrect. We need
	 * introduce another PHB callback to translate it. For now,
	 * it keeps the skiboot PCI enumeration going.
	 */
	struct npu *p = phb_to_npu(phb);
	if (p->fenced)
		*freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE;
	else
		*freeze_state = OPAL_EEH_STOPPED_NOT_FROZEN;
	return OPAL_SUCCESS;
}

static int64_t npu_eeh_next_error(struct phb *phb,
				  uint64_t *first_frozen_pe,
				  uint16_t *pci_error_type,
				  uint16_t *severity)
{
	struct npu *p = phb_to_npu(phb);
	int i;
	uint64_t result = 0;
	*first_frozen_pe = -1;
	*pci_error_type = OPAL_EEH_NO_ERROR;
	*severity = OPAL_EEH_SEV_NO_ERROR;

	if (p->fenced) {
		*pci_error_type = OPAL_EEH_PHB_ERROR;
		*severity = OPAL_EEH_SEV_PHB_FENCED;
		return OPAL_SUCCESS;
	}

	npu_ioda_sel(p, NPU_IODA_TBL_PESTB, 0, true);
	for (i = 0; i < NPU_NUM_OF_PES; i++) {
		result = in_be64(p->at_regs + NPU_IODA_DATA0);
		if (result > 0) {
			*first_frozen_pe = i;
			*pci_error_type = OPAL_EEH_PE_ERROR;
			*severity = OPAL_EEH_SEV_PE_ER;
			break;
		}
	}

	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, uint64_t pe_number,
			      uint32_t type, uint32_t func __unused,
			      uint64_t addr __unused, uint64_t mask __unused)
{
	struct npu *p = phb_to_npu(phb);
	struct npu_dev *dev = NULL;
	int i;

	if (pe_number >= NPU_NUM_OF_PES) {
		prlog(PR_ERR, "NPU: error injection failed, bad PE given\n");
		return OPAL_PARAMETER;
	}

	for (i = 0; i < p->total_devices; i++) {
		if (p->devices[i].pe_number == pe_number) {
			dev = &p->devices[i];
			break;
		}
	}

	if (!dev) {
		prlog(PR_ERR, "NPU: couldn't find device with PE%llx\n", pe_number);
		return OPAL_PARAMETER;
	}

	/* TODO: extend this to conform to OPAL injection standards */
	if (type > 1) {
		prlog(PR_ERR, "NPU: invalid error injection type\n");
		return OPAL_PARAMETER;
	} else if (type == 1) {
		/* Emulate fence mode. */
		npu_set_fence_state(p, true);
	} else {
		/* 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);
	}

	return OPAL_SUCCESS;
}

static const struct phb_ops npu_ops = {
	.cfg_read8		= npu_cfg_read8,
	.cfg_read16		= npu_cfg_read16,
	.cfg_read32		= npu_cfg_read32,
	.cfg_write8		= npu_cfg_write8,
	.cfg_write16		= npu_cfg_write16,
	.cfg_write32		= npu_cfg_write32,
	.choose_bus		= NULL,
	.get_reserved_pe_number	= NULL,
	.device_init		= NULL,
	.phb_final_fixup	= npu_phb_final_fixup,
	.ioda_reset		= npu_ioda_reset,
	.papr_errinjct_reset	= NULL,
	.pci_reinit		= NULL,
	.set_phb_mem_window	= NULL,
	.phb_mmio_enable	= NULL,
	.map_pe_mmio_window	= NULL,
	.map_pe_dma_window	= npu_map_pe_dma_window,
	.map_pe_dma_window_real	= npu_map_pe_dma_window_real,
	.pci_msi_eoi		= NULL,
	.set_xive_pe		= NULL,
	.get_msi_32		= NULL,
	.get_msi_64		= NULL,
	.set_pe			= npu_set_pe,
	.set_peltv		= NULL,
	.eeh_freeze_status	= npu_freeze_status,
	.eeh_freeze_clear	= NULL,
	.eeh_freeze_set		= NULL,
	.next_error		= npu_eeh_next_error,
	.err_inject		= npu_err_inject,
	.get_diag_data		= NULL,
	.get_diag_data2		= NULL,
	.set_capi_mode		= NULL,
	.set_capp_recovery	= NULL,
};

static void assign_mmio_bars(uint32_t gcid, uint32_t xscom,
			     struct dt_node *npu_dn, uint64_t mm_win[2],
			     uint64_t at_bar[2])
{
	uint64_t mem_start, mem_end;
	struct npu_dev_bar bar;
	struct dt_node *link;

	/* Configure BAR selection.
	 *
	 * Currently, each PHY contains 2 links and each link has 2
	 * BARs. The first BAR is assigned to the DLTL region which is
	 * what the kernel uses. The second BAR is either assigned to
	 * either the PL or AT region or unassigned. The PL0/PL1/AT
	 * MMIO regions are not exposed to the kernel so we assigned
	 * them at the start of the available memory area followed by
	 * the DLTL regions. So we end up with the following memory
	 * map (assuming we're given a memory region starting at
	 * 0x3fff000000000):
	 *
	 * Link#0-BAR#0: NTL/NDL BAR (128KB) - 0x3fff000420000
	 * Link#0-BAR#1:     PL0 BAR (  2MB) - 0x3fff000000000
	 * Link#1-BAR#0: NTL/NDL BAR (128KB) - 0x3fff000440000
	 * Link#1-BAR#1:      AT BAR ( 64KB) - 0x3fff000400000
	 * Link#2-BAR#0: NTL/NDL BAR (128KB) - 0x3fff000460000
	 * Link#2-BAR#1:     PL1 BAR (  2MB) - 0x3fff000200000
	 * Link#3-BAR#0: NTL/NDL BAR (128KB) - 0x3fff000480000
	 * Link#3-BAR#1:  UNASSIGNED
	 */
	xscom_write(gcid, xscom + NPU_AT_SCOM_OFFSET + NX_BAR,
		    0x0211000043500000UL);

	xscom_read(gcid, npu_link_scom_base(npu_dn, xscom, 0) + NX_MMIO_BAR_0,
		   &mem_start);
	mem_start = GETFIELD(NX_MMIO_BAR_BASE, mem_start) << 12;

	xscom_read(gcid, npu_link_scom_base(npu_dn, xscom, 5) + NX_MMIO_BAR_0,
		   &mem_end);
	mem_end = (GETFIELD(NX_MMIO_BAR_BASE, mem_end) << 12) +
		get_bar_size(mem_end);

	/* PL0 BAR comes first at 0x3fff000000000 */
	bar.xscom = npu_link_scom_base(npu_dn, xscom, 0) + NX_MMIO_BAR_1;
	bar.base = mem_start;
	bar.size = NX_MMIO_PL_SIZE;
	npu_dev_bar_update(gcid, &bar, true);

	/* PL1 BAR */
	bar.xscom = npu_link_scom_base(npu_dn, xscom, 4) + NX_MMIO_BAR_1;
	bar.base += bar.size;
	bar.size = NX_MMIO_PL_SIZE;
	npu_dev_bar_update(gcid, &bar, true);

	/* Then the AT BAR */
	bar.xscom = npu_link_scom_base(npu_dn, xscom, 1) + NX_MMIO_BAR_1;
	bar.base += bar.size;
	bar.size = NX_MMIO_AT_SIZE;
	at_bar[0] = bar.base;
	at_bar[1] = NX_MMIO_AT_SIZE;
	npu_dev_bar_update(gcid, &bar, true);

	/* Now we configure all the DLTL BARs. These are the ones
	 * actually exposed to the kernel. */
	mm_win[0] = bar.base + bar.size;
	dt_for_each_node(npu_dn, link) {
		uint32_t index;

		index = dt_prop_get_u32(link, "ibm,npu-link-index");
		bar.xscom = npu_link_scom_base(npu_dn, xscom, index) +
			NX_MMIO_BAR_0;
		bar.base += bar.size;
		bar.size = NX_MMIO_DL_SIZE;
		bar.base = ALIGN_UP(bar.base, bar.size);
		npu_dev_bar_update(gcid, &bar, false);
	}
	mm_win[1] = (bar.base + bar.size) - mm_win[0];

	/* If we weren't given enough room to setup all the BARs we
	 * require it's better to crash here than risk creating
	 * overlapping BARs which will xstop the machine randomly in
	 * the future.*/
	assert(bar.base + bar.size <= mem_end);
}

/* Probe NPU device node and create PCI root device node
 * accordingly. The NPU deivce node should specify number
 * of links and xscom base address to access links.
 */
static void npu_probe_phb(struct dt_node *dn)
{
	struct dt_node *np;
	uint32_t gcid, index, phb_index, xscom;
	uint64_t at_bar[2], mm_win[2];
	uint32_t links;
	char *path;

	/* Retrieve chip id */
	path = dt_get_path(dn);
	gcid = dt_get_chip_id(dn);
	index = dt_prop_get_u32(dn, "ibm,npu-index");
	phb_index = dt_prop_get_u32(dn, "ibm,phb-index");
	links = dt_prop_get_u32(dn, "ibm,npu-links");
	prlog(PR_INFO, "Chip %d Found NPU%d (%d links) at %s\n",
	      gcid, index, links, path);
	free(path);

	/* Retrieve xscom base addr */
	xscom = dt_get_address(dn, 0, NULL);
	prlog(PR_INFO, "   XSCOM Base:  %08x\n", xscom);

	assign_mmio_bars(gcid, xscom, dn, mm_win, at_bar);
	prlog(PR_INFO, "   AT BAR:      %016llx (%lldKB)\n",
	      at_bar[0], at_bar[1] / 0x400);

	/* Create PCI root device node */
	np = dt_new_addr(dt_root, "pciex", at_bar[0]);
	assert(np);

	dt_add_property_strings(np, "compatible",
				"ibm,power8-npu-pciex", "ibm,ioda2-npu-phb");
	dt_add_property_strings(np, "device_type", "pciex");
	dt_add_property(np, "reg", at_bar, sizeof(at_bar));

	dt_add_property_cells(np, "ibm,phb-index", phb_index);
	dt_add_property_cells(np, "ibm,npu-index", index);
	dt_add_property_cells(np, "ibm,chip-id", gcid);
	dt_add_property_cells(np, "ibm,xscom-base", xscom);
	dt_add_property_cells(np, "ibm,npcq", dn->phandle);
	dt_add_property_cells(np, "ibm,links", links);
	dt_add_property(np, "ibm,mmio-window", mm_win, sizeof(mm_win));
	dt_add_property_cells(np, "ibm,phb-diag-data-size", 0);

	/* Disable fast reboot - not currently supported */
	disable_fast_reboot("NVLink device enabled");
}

static void npu_dev_populate_vendor_cap(struct npu_dev_cap *cap)
{
	struct npu_dev *dev = cap->dev;
	struct pci_virt_device *pvd = dev->pvd;
	uint32_t offset = cap->start;
	uint8_t val;

	/* Add length and version information */
	val = cap->end - cap->start;
	PCI_VIRT_CFG_INIT_RO(pvd, offset + 2, 1, val);
	PCI_VIRT_CFG_INIT_RO(pvd, offset + 3, 1, OPAL_NPU_VERSION);
	offset += 4;

	/* Defaults when the trap can't handle the read/write (eg. due
	 * to reading/writing less than 4 bytes). */
	val = 0x0;
	PCI_VIRT_CFG_INIT_RO(pvd, offset, 4, val);
	PCI_VIRT_CFG_INIT_RO(pvd, offset + 4, 4, val);

	/* Create a trap for AT/PL procedures */
	pci_virt_add_filter(pvd, offset, 8,
			    PCI_REG_FLAG_READ | PCI_REG_FLAG_WRITE,
			    npu_dev_procedure, NULL);
	offset += 8;

	PCI_VIRT_CFG_INIT_RO(pvd, offset, 1, dev->index);
}

static void npu_dev_populate_pcie_cap(struct npu_dev_cap *cap)
{
	struct npu_dev *dev = cap->dev;
	struct pci_virt_device *pvd = dev->pvd;
	uint32_t base = cap->start;
	uint32_t val;

	/* Sanity check on capability ID */
	if (cap->id != PCI_CFG_CAP_ID_EXP) {
		prlog(PR_NOTICE, "%s: Invalid capability ID %d (%d)\n",
		      __func__, cap->id, PCI_CFG_CAP_ID_EXP);
		return;
	}

	/* Sanity check on spanned registers */
	if ((cap->end - cap->start) < PCIE_CAP_START) {
		prlog(PR_NOTICE, "%s: Invalid reg region [%x, %x] for cap %d\n",
		      __func__, cap->start, cap->end, cap->id);
		return;
	}

	/* 0x00 - ID/PCIE capability */
	val = cap->id;
	val |= ((0x2 << 16) | (PCIE_TYPE_ENDPOINT << 20));
	PCI_VIRT_CFG_INIT_RO(pvd, base, 4, val);

	/* 0x04 - Device capability
	 *
	 * We should support FLR. Otherwise, it might have
	 * problem passing it through to userland via Linux
	 * VFIO infrastructure
	 */
	val = ((PCIE_MPSS_128) |
	       (PCIE_PHANTOM_NONE << 3) |
	       (PCIE_L0SL_MAX_NO_LIMIT << 6) |
	       (PCIE_L1L_MAX_NO_LIMIT << 9) |
	       (PCICAP_EXP_DEVCAP_FUNC_RESET));
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_DEVCAP, 4, val);

	pci_virt_add_filter(pvd, base + PCICAP_EXP_DEVCTL, 2,
			    PCI_REG_FLAG_WRITE,
			    npu_dev_cfg_exp_devcap, NULL);

	/* 0x08 - Device control and status */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_DEVCTL, 4, 0x00002810,
			 0xffff0000, 0x000f0000);

	/* 0x0c - Link capability */
	val = (PCIE_LSPEED_VECBIT_2 | (PCIE_LWIDTH_1X << 4));
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_LCAP, 4, val);

	/* 0x10 - Link control and status */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_LCTL, 4, 0x00130000,
			 0xfffff000, 0xc0000000);

	/* 0x14 - Slot capability */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_SLOTCAP, 4, 0x00000000);

	/* 0x18 - Slot control and status */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_SLOTCTL, 4, 0x00000000);

	/* 0x1c - Root control and capability */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_RC, 4, 0x00000000,
			 0xffffffe0, 0x00000000);

	/* 0x20 - Root status */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_RSTAT, 4, 0x00000000,
			 0xffffffff, 0x00010000);

	/* 0x24 - Device capability 2 */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCIECAP_EXP_DCAP2, 4, 0x00000000);

	/* 0x28 - Device Control and status 2 */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_DCTL2, 4, 0x00070000,
			 0xffff0000, 0x00000000);

	/* 0x2c - Link capability 2 */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_LCAP2, 4, 0x00000007);

	/* 0x30 - Link control and status 2 */
	PCI_VIRT_CFG_INIT(pvd, base + PCICAP_EXP_LCTL2, 4, 0x00000003,
			 0xffff0000, 0x00200000);

	/* 0x34 - Slot capability 2 */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_SCAP2, 4, 0x00000000);

	/* 0x38 - Slot control and status 2 */
	PCI_VIRT_CFG_INIT_RO(pvd, base + PCICAP_EXP_SCTL2, 4, 0x00000000);
}

static struct npu_dev_cap *npu_dev_create_capability(struct npu_dev *dev,
				  void (*populate)(struct npu_dev_cap *),
				  uint16_t id,
				  uint16_t start,
				  uint16_t end)
{
	struct npu_dev_cap *cap;

	/* Check if the capability is existing */
	cap = npu_dev_find_capability(dev, id);
	if (cap)
		return cap;

	/* Allocate new one */
	cap = zalloc(sizeof(struct npu_dev_cap));
	assert(cap);

	/* Put it into the pool */
	cap->id         = id;
	cap->start      = start;
	cap->end        = end;
	cap->dev        = dev;
	cap->populate	= populate;
	list_add_tail(&dev->capabilities, &cap->link);

	return cap;
}

static struct npu_dev_cap *npu_dev_find_capability(struct npu_dev *dev,
						   uint16_t id)
{
	struct npu_dev_cap *cap;

	list_for_each(&dev->capabilities, cap, link) {
		if (cap->id == id)
			return cap;
	}

	return NULL;
}

/*
 * All capabilities should be put into the device capability
 * list according to register offset in ascending order for
 * easy access at later point.
 */
static void npu_dev_create_capabilities(struct npu_dev *dev)
{
	list_head_init(&dev->capabilities);

	/* PCI express capability */
	npu_dev_create_capability(dev, npu_dev_populate_pcie_cap,
				  PCI_CFG_CAP_ID_EXP, PCIE_CAP_START,
				  PCIE_CAP_END);

	/* Vendor specific capability */
	npu_dev_create_capability(dev, npu_dev_populate_vendor_cap,
				  PCI_CFG_CAP_ID_VENDOR, VENDOR_CAP_START,
				  VENDOR_CAP_END);
}

static void npu_dev_create_cfg(struct npu_dev *dev)
{
	struct pci_virt_device *pvd = dev->pvd;
	struct npu_dev_cap *cap;
	uint32_t offset;
	uint32_t last_cap_offset;

	/* 0x00 - Vendor/Device ID */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_VENDOR_ID, 4, 0x04ea1014);

	/* 0x04 - Command/Status
	 *
	 * Create one trap to trace toggling memory BAR enable bit
	 */
	PCI_VIRT_CFG_INIT(pvd, PCI_CFG_CMD, 4, 0x00100000, 0xffb802b8,
			 0xf9000000);

	pci_virt_add_filter(pvd, PCI_CFG_CMD, 1, PCI_REG_FLAG_WRITE,
			    npu_dev_cfg_write_cmd, NULL);

	/* 0x08 - Rev/Class/Cache */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_REV_ID, 4, 0x06800100);

	/* 0x0c - CLS/Latency Timer/Header/BIST */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_CACHE_LINE_SIZE, 4, 0x00800000);

	/* 0x10 - BARs, always 64-bits non-prefetchable
	 *
	 * Each emulated device represents one link and therefore
	 * there is one BAR for the associated DLTL region.
	 */

	/* Low 32-bits */
	PCI_VIRT_CFG_INIT(pvd, PCI_CFG_BAR0, 4,
			 (dev->bar.base & 0xfffffff0) | dev->bar.flags,
			 0x0000000f, 0x00000000);

	/* High 32-bits */
	PCI_VIRT_CFG_INIT(pvd, PCI_CFG_BAR1, 4, (dev->bar.base >> 32),
			 0x00000000, 0x00000000);

	/*
	 * Create trap. Writting 0xFF's to BAR registers should be
	 * trapped and return size on next read
	 */
	pci_virt_add_filter(pvd, PCI_CFG_BAR0, 8,
			    PCI_REG_FLAG_READ | PCI_REG_FLAG_WRITE,
			    npu_dev_cfg_bar, &dev->bar);

	/* 0x18/1c/20/24 - Disabled BAR#2/3/4/5
	 *
	 * Mark those BARs readonly so that 0x0 will be returned when
	 * probing the length and the BARs will be skipped.
	 */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_BAR2, 4, 0x00000000);
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_BAR3, 4, 0x00000000);
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_BAR4, 4, 0x00000000);
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_BAR5, 4, 0x00000000);

	/* 0x28 - Cardbus CIS pointer */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_CARDBUS_CIS, 4, 0x00000000);

	/* 0x2c - Subsystem ID */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_SUBSYS_VENDOR_ID, 4, 0x00000000);

	/* 0x30 - ROM BAR
	 *
	 * Force its size to be zero so that the kernel will skip
	 * probing the ROM BAR. We needn't emulate ROM BAR.
	 */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_ROMBAR, 4, 0xffffffff);

	/* 0x34 - PCI Capability
	 *
	 * By default, we don't have any capabilities
	 */
	PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_CAP, 4, 0x00000000);

	last_cap_offset = PCI_CFG_CAP - 1;
	list_for_each(&dev->capabilities, cap, link) {
		offset = cap->start;

		/* Initialize config space for the capability */
		if (cap->populate)
			cap->populate(cap);

		/* Add capability header */
		PCI_VIRT_CFG_INIT_RO(pvd, offset, 2, cap->id);

		/* Update the next capability pointer */
		PCI_VIRT_CFG_NORMAL_WR(pvd, last_cap_offset + 1, 1, offset);

		last_cap_offset = offset;
	}

	/* 0x38 - Reserved */
	PCI_VIRT_CFG_INIT_RO(pvd, 0x38, 4, 0x00000000);

	/* 0x3c - INT line/pin/Minimal grant/Maximal latency */
	if (!(dev->index % 2))
		PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_INT_LINE, 4, 0x00000100);
	else
		PCI_VIRT_CFG_INIT_RO(pvd, PCI_CFG_INT_LINE, 4, 0x00000200);
}

static uint32_t npu_allocate_bdfn(struct npu *p, uint32_t group)
{
	int i;
	int bdfn = (group << 3);

	for (i = 0; i < p->total_devices; i++) {
		if ((p->devices[i].pvd->bdfn & 0xf8) == (bdfn & 0xf8))
			bdfn++;
	}

	return bdfn;
}

static void npu_create_devices(struct dt_node *dn, struct npu *p)
{
	struct npu_dev *dev;
	struct dt_node *npu_dn, *link;
	uint32_t bdfn, npu_phandle, index = 0;
	uint64_t buid_reg;
	uint64_t lsisrcid;
	uint64_t buid;


	/* The bits in the LSI ID Base register are always compared and
	 * can be set to 0 in the buid base and mask fields.  The
	 * buid (bus unit id) is the full irq minus the last 4 bits. */
	lsisrcid = GETFIELD(NPU_LSI_SRC_ID_BASE, NPU_LSI_SRC_ID_BASE);
	buid = p8_chip_irq_block_base(p->chip_id, P8_IRQ_BLOCK_MISC) >> 4;

	buid_reg = SETFIELD(NP_IRQ_LEVELS, NP_BUID_ENABLE, ~0);
	buid_reg = SETFIELD(NP_BUID_MASK, buid_reg, ~lsisrcid);
	buid_reg = SETFIELD(NP_BUID_BASE, buid_reg, (buid & ~lsisrcid));

	/* Get the npu node which has the links which we expand here
	 * into pci like devices attached to our emulated phb. */
	npu_phandle = dt_prop_get_u32(dn, "ibm,npcq");
	npu_dn = dt_find_by_phandle(dt_root, npu_phandle);
	assert(npu_dn);

	/* Walk the link@x nodes to initialize devices */
	p->total_devices = 0;
	p->phb.scan_map = 0;
	list_head_init(&p->phb.virt_devices);
	dt_for_each_compatible(npu_dn, link, "ibm,npu-link") {
		struct npu_dev_bar *bar;
		uint32_t group_id;
		uint64_t val;

		dev = &p->devices[index];
		dev->index = dt_prop_get_u32(link, "ibm,npu-link-index");
		dev->xscom = npu_link_scom_base(npu_dn, p->xscom_base,
						dev->index);

		dev->npu = p;
		dev->dt_node = link;

		/* We don't support MMIO PHY access yet */
		dev->pl_base = NULL;

		group_id = dt_prop_get_u32(link, "ibm,npu-group-id");
		bdfn = npu_allocate_bdfn(p, group_id);

		/* This must be done after calling
		 * npu_allocate_bdfn() */
		p->total_devices++;
		p->phb.scan_map |= 0x1 << ((bdfn & 0xf8) >> 3);

		dev->pl_xscom_base = dt_prop_get_u64(link, "ibm,npu-phy");
		dev->lane_mask = dt_prop_get_u32(link, "ibm,npu-lane-mask");

		/* Setup BUID/ISRN */
		xscom_write(p->chip_id, dev->xscom + NX_NP_BUID, buid_reg);

		/* Create PCI virtual device */
		dev->pvd = pci_virt_add_device(&p->phb, bdfn, NPU_DEV_CFG_SIZE, dev);
		assert(dev->pvd);
		bar = &dev->bar;
		bar->flags = (PCI_CFG_BAR_TYPE_MEM |
			      PCI_CFG_BAR_MEM64);

		/* Update BAR info */
		bar->xscom = dev->xscom + NX_MMIO_BAR_0;
		xscom_read(p->chip_id, bar->xscom, &val);
		bar->base  = GETFIELD(NX_MMIO_BAR_BASE, val) << 12;
		bar->size = get_bar_size(val);

		/*
		 * The config space is initialised with the BARs
		 * disabled, so make sure it is actually disabled in
		 * hardware.
		 */
		npu_dev_bar_update(p->chip_id, bar, false);

		/* Initialize capabilities */
		npu_dev_create_capabilities(dev);

		/* Initialize config space */
		npu_dev_create_cfg(dev);

		index++;
	}
}

static void npu_add_phb_properties(struct npu *p)
{
	struct dt_node *np = p->phb.dt_node;
	uint32_t icsp = get_ics_phandle();
	uint64_t tkill, mm_base, mm_size;
	uint32_t base_lsi = p->base_lsi;
	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
	 * policy decisions made in skiboot rather than in HB
	 * such as the MMIO windows going to PCI, interrupts,
	 * etc.
	 */
	dt_add_property_cells(np, "#address-cells", 3);
	dt_add_property_cells(np, "#size-cells", 2);
	dt_add_property_cells(np, "#interrupt-cells", 1);
	dt_add_property_cells(np, "bus-range", 0, 0xff);
	dt_add_property_cells(np, "clock-frequency", 0x200, 0);
        dt_add_property_cells(np, "interrupt-parent", icsp);

        /* 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));

	/* NPU PHB properties */
	/* TODO: Due to an errata TCE KILL only works when DMA traffic
	 * has been stopped. We need to implement the work around
	 * which is to do a TCE kill all instead. */
	tkill = cleanup_addr((uint64_t)p->at_regs) + NPU_TCE_KILL;
	dt_add_property_cells(np, "ibm,opal-num-pes",
			      NPU_NUM_OF_PES);
	dt_add_property_cells(np, "ibm,opal-reserved-pe",
			      0);
        dt_add_property_u64(np, "ibm,opal-tce-kill", tkill);

	/* Memory window is exposed as 32-bits non-prefetchable
	 * one because 64-bits prefetchable one is kind of special
	 * to kernel.
	 */
	mm_base = p->mm_base;
	mm_size = p->mm_size;
	dt_add_property_cells(np, "ranges", 0x02000000,
			      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;

	/* Retrieve number of devices */
	links = dt_prop_get_u32(dn, "ibm,links");
	pmem = zalloc(sizeof(struct npu) + links * sizeof(struct npu_dev));
	assert(pmem);

	/* Populate PHB */
	p = pmem;
	p->index = dt_prop_get_u32(dn, "ibm,npu-index");
	p->chip_id = dt_prop_get_u32(dn, "ibm,chip-id");
	p->xscom_base = dt_prop_get_u32(dn, "ibm,xscom-base");
	p->total_devices = links;

	/* TODO: When hardware fences are implemented, detect them here */
	p->fenced = false;

	/* This is the AT base */
	p->at_xscom = p->xscom_base + NPU_AT_SCOM_OFFSET;
	p->at_regs = (void *)dt_get_address(dn, 0, NULL);

	prop = dt_require_property(dn, "ibm,mmio-window", -1);
	assert(prop->len >= (2 * sizeof(uint64_t)));
	p->mm_base = ((const uint64_t *)prop->prop)[0];
	p->mm_size = ((const uint64_t *)prop->prop)[1];

	p->devices = pmem + sizeof(struct npu);

	/* Interrupt */
        p->base_lsi = p8_chip_irq_block_base(p->chip_id, P8_IRQ_BLOCK_MISC) +
		NPU_LSI_IRQ_MIN;

	/* Generic PHB */
	p->phb.dt_node = dn;
	p->phb.ops = &npu_ops;
	p->phb.phb_type = phb_type_pcie_v3;

	/* Populate devices */
	npu_create_devices(dn, p);

	/* 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);

	/* Initialize IODA cache */
	npu_ioda_init(p);

	/* Register interrupt source */
	npu_register_irq(p);

	/* Initialize hardware */
	npu_hw_init(p);
}

void probe_npu(void)
{
	struct dt_node *np;

	/* Scan NPU XSCOM nodes */
	dt_for_each_compatible(dt_root, np, "ibm,power8-npu")
		npu_probe_phb(np);

	/* Scan newly created PHB nodes */
	dt_for_each_compatible(dt_root, np, "ibm,power8-npu-pciex")
		npu_create_phb(np);
}
