// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2018 Texas Instruments, Inc
 */

#include <common.h>
#include <dm.h>
#include <pci.h>
#include <generic-phy.h>
#include <power-domain.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm-generic/gpio.h>

DECLARE_GLOBAL_DATA_PTR;

#define PCIE_VENDORID_MASK	GENMASK(15, 0)
#define PCIE_DEVICEID_SHIFT	16

/* PCI DBICS registers */
#define PCIE_CONFIG_BAR0		0x10
#define PCIE_LINK_STATUS_REG		0x80
#define PCIE_LINK_STATUS_SPEED_OFF	16
#define PCIE_LINK_STATUS_SPEED_MASK	(0xf << PCIE_LINK_STATUS_SPEED_OFF)
#define PCIE_LINK_STATUS_WIDTH_OFF	20
#define PCIE_LINK_STATUS_WIDTH_MASK	(0xf << PCIE_LINK_STATUS_WIDTH_OFF)

#define PCIE_LINK_CAPABILITY		0x7c
#define PCIE_LINK_CTL_2			0xa0
#define TARGET_LINK_SPEED_MASK		0xf
#define LINK_SPEED_GEN_1		0x1
#define LINK_SPEED_GEN_2		0x2
#define LINK_SPEED_GEN_3		0x3

#define PCIE_MISC_CONTROL_1_OFF		0x8bc
#define PCIE_DBI_RO_WR_EN		BIT(0)

#define PLR_OFFSET			0x700
#define PCIE_PORT_DEBUG0		(PLR_OFFSET + 0x28)
#define PORT_LOGIC_LTSSM_STATE_MASK	0x1f
#define PORT_LOGIC_LTSSM_STATE_L0	0x11

#define PCIE_LINK_WIDTH_SPEED_CONTROL	0x80c
#define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)

#define PCIE_LINK_UP_TIMEOUT_MS		100

/*
 * iATU Unroll-specific register definitions
 * From 4.80 core version the address translation will be made by unroll.
 * The registers are offset from atu_base
 */
#define PCIE_ATU_UNR_REGION_CTRL1	0x00
#define PCIE_ATU_UNR_REGION_CTRL2	0x04
#define PCIE_ATU_UNR_LOWER_BASE		0x08
#define PCIE_ATU_UNR_UPPER_BASE		0x0c
#define PCIE_ATU_UNR_LIMIT		0x10
#define PCIE_ATU_UNR_LOWER_TARGET	0x14
#define PCIE_ATU_UNR_UPPER_TARGET	0x18

#define PCIE_ATU_REGION_INDEX1		(0x1 << 0)
#define PCIE_ATU_REGION_INDEX0		(0x0 << 0)
#define PCIE_ATU_TYPE_MEM		(0x0 << 0)
#define PCIE_ATU_TYPE_IO		(0x2 << 0)
#define PCIE_ATU_TYPE_CFG0		(0x4 << 0)
#define PCIE_ATU_TYPE_CFG1		(0x5 << 0)
#define PCIE_ATU_ENABLE			(0x1 << 31)
#define PCIE_ATU_BAR_MODE_ENABLE	(0x1 << 30)
#define PCIE_ATU_BUS(x)			(((x) & 0xff) << 24)
#define PCIE_ATU_DEV(x)			(((x) & 0x1f) << 19)
#define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)

/* Register address builder */
#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region)	((region) << 9)

/* Offsets from App base */
#define PCIE_CMD_STATUS			0x04
#define LTSSM_EN_VAL			BIT(0)

/* Parameters for the waiting for iATU enabled routine */
#define LINK_WAIT_MAX_IATU_RETRIES	5
#define LINK_WAIT_IATU			10000

#define AM654_PCIE_DEV_TYPE_MASK	0x3
#define EP				0x0
#define LEG_EP				0x1
#define RC				0x2

/**
 * struct pcie_dw_ti - TI DW PCIe controller state
 *
 * @app_base: The base address of application register space
 * @dbics_base: The base address of dbics register space
 * @cfg_base: The base address of configuration space
 * @atu_base: The base address of ATU space
 * @cfg_size: The size of the configuration space which is needed
 *            as it gets written into the PCIE_ATU_LIMIT register
 * @first_busno: This driver supports multiple PCIe controllers.
 *               first_busno stores the bus number of the PCIe root-port
 *               number which may vary depending on the PCIe setup
 *               (PEX switches etc).
 */
struct pcie_dw_ti {
	void *app_base;
	void *dbi_base;
	void *cfg_base;
	void *atu_base;
	fdt_size_t cfg_size;
	int first_busno;
	struct udevice *dev;

	/* IO and MEM PCI regions */
	struct pci_region io;
	struct pci_region mem;
};

enum dw_pcie_device_mode {
	DW_PCIE_UNKNOWN_TYPE,
	DW_PCIE_EP_TYPE,
	DW_PCIE_LEG_EP_TYPE,
	DW_PCIE_RC_TYPE,
};

static int pcie_dw_get_link_speed(struct pcie_dw_ti *pci)
{
	return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) &
		PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF;
}

static int pcie_dw_get_link_width(struct pcie_dw_ti *pci)
{
	return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) &
		PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF;
}

static void dw_pcie_writel_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg,
				     u32 val)
{
	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
	void __iomem *base = pci->atu_base;

	writel(val, base + offset + reg);
}

static u32 dw_pcie_readl_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg)
{
	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
	void __iomem *base = pci->atu_base;

	return readl(base + offset + reg);
}

/**
 * pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses
 *
 * @pcie: Pointer to the PCI controller state
 * @index: ATU region index
 * @type: ATU accsess type
 * @cpu_addr: the physical address for the translation entry
 * @pci_addr: the pcie bus address for the translation entry
 * @size: the size of the translation entry
 */
static void pcie_dw_prog_outbound_atu_unroll(struct pcie_dw_ti *pci, int index,
					     int type, u64 cpu_addr,
					     u64 pci_addr, u32 size)
{
	u32 retries, val;

	debug("ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n",
	      index, type, cpu_addr, pci_addr, size);

	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
				 lower_32_bits(cpu_addr));
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
				 upper_32_bits(cpu_addr));
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
				 lower_32_bits(cpu_addr + size - 1));
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
				 lower_32_bits(pci_addr));
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
				 upper_32_bits(pci_addr));
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
				 type);
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
				 PCIE_ATU_ENABLE);

	/*
	 * Make sure ATU enable takes effect before any subsequent config
	 * and I/O accesses.
	 */
	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
		val = dw_pcie_readl_ob_unroll(pci, index,
					      PCIE_ATU_UNR_REGION_CTRL2);
		if (val & PCIE_ATU_ENABLE)
			return;

		udelay(LINK_WAIT_IATU);
	}
	dev_err(pci->dev, "outbound iATU is not being enabled\n");
}

/**
 * set_cfg_address() - Configure the PCIe controller config space access
 *
 * @pcie: Pointer to the PCI controller state
 * @d: PCI device to access
 * @where: Offset in the configuration space
 *
 * Configures the PCIe controller to access the configuration space of
 * a specific PCIe device and returns the address to use for this
 * access.
 *
 * Return: Address that can be used to access the configation space
 *         of the requested device / offset
 */
static uintptr_t set_cfg_address(struct pcie_dw_ti *pcie,
				 pci_dev_t d, uint where)
{
	int bus = PCI_BUS(d) - pcie->first_busno;
	uintptr_t va_address;
	u32 atu_type;

	/* Use dbi_base for own configuration read and write */
	if (!bus) {
		va_address = (uintptr_t)pcie->dbi_base;
		goto out;
	}

	if (bus == 1)
		/* For local bus, change TLP Type field to 4. */
		atu_type = PCIE_ATU_TYPE_CFG0;
	else
		/* Otherwise, change TLP Type field to 5. */
		atu_type = PCIE_ATU_TYPE_CFG1;

	/*
	 * Not accessing root port configuration space?
	 * Region #0 is used for Outbound CFG space access.
	 * Direction = Outbound
	 * Region Index = 0
	 */
	d = PCI_MASK_BUS(d);
	d = PCI_ADD_BUS(bus, d);
	pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
					 atu_type, (u64)pcie->cfg_base,
					 d << 8, pcie->cfg_size);

	va_address = (uintptr_t)pcie->cfg_base;

out:
	va_address += where & ~0x3;

	return va_address;
}

/**
 * pcie_dw_addr_valid() - Check for valid bus address
 *
 * @d: The PCI device to access
 * @first_busno: Bus number of the PCIe controller root complex
 *
 * Return 1 (true) if the PCI device can be accessed by this controller.
 *
 * Return: 1 on valid, 0 on invalid
 */
static int pcie_dw_addr_valid(pci_dev_t d, int first_busno)
{
	if ((PCI_BUS(d) == first_busno) && (PCI_DEV(d) > 0))
		return 0;
	if ((PCI_BUS(d) == first_busno + 1) && (PCI_DEV(d) > 0))
		return 0;

	return 1;
}

/**
 * pcie_dw_ti_read_config() - Read from configuration space
 *
 * @bus: Pointer to the PCI bus
 * @bdf: Identifies the PCIe device to access
 * @offset: The offset into the device's configuration space
 * @valuep: A pointer at which to store the read value
 * @size: Indicates the size of access to perform
 *
 * Read a value of size @size from offset @offset within the configuration
 * space of the device identified by the bus, device & function numbers in @bdf
 * on the PCI bus @bus.
 *
 * Return: 0 on success
 */
static int pcie_dw_ti_read_config(struct udevice *bus, pci_dev_t bdf,
				  uint offset, ulong *valuep,
				  enum pci_size_t size)
{
	struct pcie_dw_ti *pcie = dev_get_priv(bus);
	uintptr_t va_address;
	ulong value;

	debug("PCIE CFG read: bdf=%2x:%2x:%2x ",
	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));

	if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
		debug("- out of range\n");
		*valuep = pci_get_ff(size);
		return 0;
	}

	va_address = set_cfg_address(pcie, bdf, offset);

	value = readl(va_address);

	debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
	*valuep = pci_conv_32_to_size(value, offset, size);

	pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
					 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
					 pcie->io.bus_start, pcie->io.size);

	return 0;
}

/**
 * pcie_dw_ti_write_config() - Write to configuration space
 *
 * @bus: Pointer to the PCI bus
 * @bdf: Identifies the PCIe device to access
 * @offset: The offset into the device's configuration space
 * @value: The value to write
 * @size: Indicates the size of access to perform
 *
 * Write the value @value of size @size from offset @offset within the
 * configuration space of the device identified by the bus, device & function
 * numbers in @bdf on the PCI bus @bus.
 *
 * Return: 0 on success
 */
static int pcie_dw_ti_write_config(struct udevice *bus, pci_dev_t bdf,
				   uint offset, ulong value,
				   enum pci_size_t size)
{
	struct pcie_dw_ti *pcie = dev_get_priv(bus);
	uintptr_t va_address;
	ulong old;

	debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
	debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);

	if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
		debug("- out of range\n");
		return 0;
	}

	va_address = set_cfg_address(pcie, bdf, offset);

	old = readl(va_address);
	value = pci_conv_size_to_32(old, value, offset, size);
	writel(value, va_address);

	pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
					 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
					 pcie->io.bus_start, pcie->io.size);

	return 0;
}

static inline void dw_pcie_dbi_write_enable(struct pcie_dw_ti *pci, bool en)
{
	u32 val;

	val = readl(pci->dbi_base + PCIE_MISC_CONTROL_1_OFF);
	if (en)
		val |= PCIE_DBI_RO_WR_EN;
	else
		val &= ~PCIE_DBI_RO_WR_EN;
	writel(val, pci->dbi_base + PCIE_MISC_CONTROL_1_OFF);
}

/**
 * pcie_dw_configure() - Configure link capabilities and speed
 *
 * @regs_base: A pointer to the PCIe controller registers
 * @cap_speed: The capabilities and speed to configure
 *
 * Configure the link capabilities and speed in the PCIe root complex.
 */
static void pcie_dw_configure(struct pcie_dw_ti *pci, u32 cap_speed)
{
	u32 val;

	dw_pcie_dbi_write_enable(pci, true);

	val = readl(pci->dbi_base + PCIE_LINK_CAPABILITY);
	val &= ~TARGET_LINK_SPEED_MASK;
	val |= cap_speed;
	writel(val, pci->dbi_base + PCIE_LINK_CAPABILITY);

	val = readl(pci->dbi_base + PCIE_LINK_CTL_2);
	val &= ~TARGET_LINK_SPEED_MASK;
	val |= cap_speed;
	writel(val, pci->dbi_base + PCIE_LINK_CTL_2);

	dw_pcie_dbi_write_enable(pci, false);
}

/**
 * is_link_up() - Return the link state
 *
 * @regs_base: A pointer to the PCIe DBICS registers
 *
 * Return: 1 (true) for active line and 0 (false) for no link
 */
static int is_link_up(struct pcie_dw_ti *pci)
{
	u32 val;

	val = readl(pci->dbi_base + PCIE_PORT_DEBUG0);
	val &= PORT_LOGIC_LTSSM_STATE_MASK;

	return (val == PORT_LOGIC_LTSSM_STATE_L0);
}

/**
 * wait_link_up() - Wait for the link to come up
 *
 * @regs_base: A pointer to the PCIe controller registers
 *
 * Return: 1 (true) for active line and 0 (false) for no link (timeout)
 */
static int wait_link_up(struct pcie_dw_ti *pci)
{
	unsigned long timeout;

	timeout = get_timer(0) + PCIE_LINK_UP_TIMEOUT_MS;
	while (!is_link_up(pci)) {
		if (get_timer(0) > timeout)
			return 0;
	};

	return 1;
}

static int pcie_dw_ti_pcie_link_up(struct pcie_dw_ti *pci, u32 cap_speed)
{
	u32 val;

	if (is_link_up(pci)) {
		printf("PCI Link already up before configuration!\n");
		return 1;
	}

	/* DW pre link configurations */
	pcie_dw_configure(pci, cap_speed);

	/* Initiate link training */
	val = readl(pci->app_base + PCIE_CMD_STATUS);
	val |= LTSSM_EN_VAL;
	writel(val, pci->app_base + PCIE_CMD_STATUS);

	/* Check that link was established */
	if (!wait_link_up(pci))
		return 0;

	/*
	 * Link can be established in Gen 1. still need to wait
	 * till MAC nagaotiation is completed
	 */
	udelay(100);

	return 1;
}

/**
 * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion
 *
 * @pcie: Pointer to the PCI controller state
 *
 * Configure the host BARs of the PCIe controller root port so that
 * PCI(e) devices may access the system memory.
 */
static void pcie_dw_setup_host(struct pcie_dw_ti *pci)
{
	u32 val;

	/* setup RC BARs */
	writel(PCI_BASE_ADDRESS_MEM_TYPE_64,
	       pci->dbi_base + PCI_BASE_ADDRESS_0);
	writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1);

	/* setup interrupt pins */
	dw_pcie_dbi_write_enable(pci, true);
	val = readl(pci->dbi_base + PCI_INTERRUPT_LINE);
	val &= 0xffff00ff;
	val |= 0x00000100;
	writel(val, pci->dbi_base + PCI_INTERRUPT_LINE);
	dw_pcie_dbi_write_enable(pci, false);

	/* setup bus numbers */
	val = readl(pci->dbi_base + PCI_PRIMARY_BUS);
	val &= 0xff000000;
	val |= 0x00ff0100;
	writel(val, pci->dbi_base + PCI_PRIMARY_BUS);

	/* setup command register */
	val = readl(pci->dbi_base + PCI_COMMAND);
	val &= 0xffff0000;
	val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
	writel(val, pci->dbi_base + PCI_COMMAND);

	/* Enable write permission for the DBI read-only register */
	dw_pcie_dbi_write_enable(pci, true);
	/* program correct class for RC */
	writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE);
	/* Better disable write permission right after the update */
	dw_pcie_dbi_write_enable(pci, false);

	val = readl(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
	val |= PORT_LOGIC_SPEED_CHANGE;
	writel(val, pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
}

static int pcie_am654_set_mode(struct pcie_dw_ti *pci,
			       enum dw_pcie_device_mode mode)
{
	struct regmap *syscon;
	u32 val;
	u32 mask;
	int ret;

	syscon = syscon_regmap_lookup_by_phandle(pci->dev,
						 "ti,syscon-pcie-mode");
	if (IS_ERR(syscon))
		return 0;

	mask = AM654_PCIE_DEV_TYPE_MASK;

	switch (mode) {
	case DW_PCIE_RC_TYPE:
		val = RC;
		break;
	case DW_PCIE_EP_TYPE:
		val = EP;
		break;
	default:
		dev_err(pci->dev, "INVALID device type %d\n", mode);
		return -EINVAL;
	}

	ret = regmap_update_bits(syscon, 0, mask, val);
	if (ret) {
		dev_err(pci->dev, "failed to set pcie mode\n");
		return ret;
	}

	return 0;
}

static int pcie_dw_init_id(struct pcie_dw_ti *pci)
{
	struct regmap *devctrl_regs;
	unsigned int id;
	int ret;

	devctrl_regs = syscon_regmap_lookup_by_phandle(pci->dev,
						       "ti,syscon-pcie-id");
	if (IS_ERR(devctrl_regs))
		return PTR_ERR(devctrl_regs);

	ret = regmap_read(devctrl_regs, 0, &id);
	if (ret)
		return ret;

	dw_pcie_dbi_write_enable(pci, true);
	writew(id & PCIE_VENDORID_MASK, pci->dbi_base + PCI_VENDOR_ID);
	writew(id >> PCIE_DEVICEID_SHIFT, pci->dbi_base + PCI_DEVICE_ID);
	dw_pcie_dbi_write_enable(pci, false);

	return 0;
}

/**
 * pcie_dw_ti_probe() - Probe the PCIe bus for active link
 *
 * @dev: A pointer to the device being operated on
 *
 * Probe for an active link on the PCIe bus and configure the controller
 * to enable this port.
 *
 * Return: 0 on success, else -ENODEV
 */
static int pcie_dw_ti_probe(struct udevice *dev)
{
	struct pcie_dw_ti *pci = dev_get_priv(dev);
	struct udevice *ctlr = pci_get_controller(dev);
	struct pci_controller *hose = dev_get_uclass_priv(ctlr);
	struct power_domain pci_pwrdmn;
	struct phy phy0, phy1;
	int ret;

	ret = power_domain_get_by_index(dev, &pci_pwrdmn, 0);
	if (ret) {
		dev_err(dev, "failed to get power domain\n");
		return ret;
	}

	ret = power_domain_on(&pci_pwrdmn);
	if (ret) {
		dev_err(dev, "Power domain on failed\n");
		return ret;
	}

	ret = generic_phy_get_by_name(dev,  "pcie-phy0", &phy0);
	if (ret) {
		dev_err(dev, "Unable to get phy0");
		return ret;
	}
	generic_phy_reset(&phy0);
	generic_phy_init(&phy0);
	generic_phy_power_on(&phy0);

	ret = generic_phy_get_by_name(dev,  "pcie-phy1", &phy1);
	if (ret) {
		dev_err(dev, "Unable to get phy1");
		return ret;
	}
	generic_phy_reset(&phy1);
	generic_phy_init(&phy1);
	generic_phy_power_on(&phy1);

	pci->first_busno = dev->seq;
	pci->dev = dev;

	pcie_dw_setup_host(pci);
	pcie_dw_init_id(pci);

	if (device_is_compatible(dev, "ti,am654-pcie-rc"))
		pcie_am654_set_mode(pci, DW_PCIE_RC_TYPE);

	if (!pcie_dw_ti_pcie_link_up(pci, LINK_SPEED_GEN_2)) {
		printf("PCIE-%d: Link down\n", dev->seq);
		return -ENODEV;
	}

	printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev->seq,
	       pcie_dw_get_link_speed(pci),
	       pcie_dw_get_link_width(pci),
	       hose->first_busno);

	/* Store the IO and MEM windows settings for future use by the ATU */
	pci->io.phys_start = hose->regions[0].phys_start; /* IO base */
	pci->io.bus_start  = hose->regions[0].bus_start;  /* IO_bus_addr */
	pci->io.size	    = hose->regions[0].size;	   /* IO size */

	pci->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
	pci->mem.bus_start  = hose->regions[1].bus_start;  /* MEM_bus_addr */
	pci->mem.size	     = hose->regions[1].size;	    /* MEM size */

	pcie_dw_prog_outbound_atu_unroll(pci, PCIE_ATU_REGION_INDEX0,
					 PCIE_ATU_TYPE_MEM,
					 pci->mem.phys_start,
					 pci->mem.bus_start, pci->mem.size);

	return 0;
}

/**
 * pcie_dw_ti_ofdata_to_platdata() - Translate from DT to device state
 *
 * @dev: A pointer to the device being operated on
 *
 * Translate relevant data from the device tree pertaining to device @dev into
 * state that the driver will later make use of. This state is stored in the
 * device's private data structure.
 *
 * Return: 0 on success, else -EINVAL
 */
static int pcie_dw_ti_ofdata_to_platdata(struct udevice *dev)
{
	struct pcie_dw_ti *pcie = dev_get_priv(dev);

	/* Get the controller base address */
	pcie->dbi_base = (void *)dev_read_addr_name(dev, "dbics");
	if ((fdt_addr_t)pcie->dbi_base == FDT_ADDR_T_NONE)
		return -EINVAL;

	/* Get the config space base address and size */
	pcie->cfg_base = (void *)dev_read_addr_size_name(dev, "config",
							 &pcie->cfg_size);
	if ((fdt_addr_t)pcie->cfg_base == FDT_ADDR_T_NONE)
		return -EINVAL;

	/* Get the iATU base address and size */
	pcie->atu_base = (void *)dev_read_addr_name(dev, "atu");
	if ((fdt_addr_t)pcie->atu_base == FDT_ADDR_T_NONE)
		return -EINVAL;

	/* Get the app base address and size */
	pcie->app_base = (void *)dev_read_addr_name(dev, "app");
	if ((fdt_addr_t)pcie->app_base == FDT_ADDR_T_NONE)
		return -EINVAL;

	return 0;
}

static const struct dm_pci_ops pcie_dw_ti_ops = {
	.read_config	= pcie_dw_ti_read_config,
	.write_config	= pcie_dw_ti_write_config,
};

static const struct udevice_id pcie_dw_ti_ids[] = {
	{ .compatible = "ti,am654-pcie-rc" },
	{ }
};

U_BOOT_DRIVER(pcie_dw_ti) = {
	.name			= "pcie_dw_ti",
	.id			= UCLASS_PCI,
	.of_match		= pcie_dw_ti_ids,
	.ops			= &pcie_dw_ti_ops,
	.ofdata_to_platdata	= pcie_dw_ti_ofdata_to_platdata,
	.probe			= pcie_dw_ti_probe,
	.priv_auto_alloc_size	= sizeof(struct pcie_dw_ti),
};