pci: pci_mvebu: Add DM_PCI support and move CONFIG_PCI_MVEBU to defconfig
This patch adds DM_PCI support to the MVEBU PCIe driver. This is
necessary, since all PCI drivers have to be moved to DM (driver model)
until the v2019.07 release.
To not break git bisect'ablility, this patch also moves CONFIG_PCI_MVEBU
from config headers to the defconfig files.
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Dirk Eibach <dirk.eibach@gdsys.cc>
Cc: Mario Six <mario.six@gdsys.cc>
Cc: Chris Packham <chris.packham@alliedtelesis.co.nz>
Cc: Phil Sutter <phil@nwl.cc>
Cc: Marek BehĂșn <marek.behun@nic.cz>
Cc: VlaoMao <vlaomao@gmail.com>
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index f59803d..1521885 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -112,4 +112,13 @@
Say Y here if you want to enable PCIe controller support on Intel
FPGA, example Stratix 10.
+config PCI_MVEBU
+ bool "Enable Armada XP/38x PCIe driver"
+ depends on ARCH_MVEBU
+ select DM_PCI
+ select MISC
+ help
+ Say Y here if you want to enable PCIe controller support on
+ Armada XP/38x SoCs.
+
endif
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c
index affe9f9..6026fa6 100644
--- a/drivers/pci/pci_mvebu.c
+++ b/drivers/pci/pci_mvebu.c
@@ -10,11 +10,16 @@
*/
#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/of_access.h>
#include <pci.h>
-#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
#include <linux/mbus.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -59,26 +64,22 @@
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)
-struct resource {
- u32 start;
- u32 end;
-};
-
struct mvebu_pcie {
struct pci_controller hose;
- char *name;
void __iomem *base;
void __iomem *membase;
struct resource mem;
void __iomem *iobase;
u32 port;
u32 lane;
+ int devfn;
u32 lane_mask;
pci_dev_t dev;
+ char name[16];
+ unsigned int mem_target;
+ unsigned int mem_attr;
};
-#define to_pcie(_hc) container_of(_hc, struct mvebu_pcie, pci)
-
/*
* MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped
* into SoCs address space. Each controller will map 128M of MEM
@@ -87,82 +88,6 @@
static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE;
#define PCIE_MEM_SIZE (128 << 20)
-#if defined(CONFIG_ARMADA_38X)
-#define PCIE_BASE(if) \
- ((if) == 0 ? \
- MVEBU_REG_PCIE0_BASE : \
- (MVEBU_REG_PCIE_BASE + 0x4000 * (if - 1)))
-
-/*
- * On A38x MV6820 these PEX ports are supported:
- * 0 - Port 0.0
- * 1 - Port 1.0
- * 2 - Port 2.0
- * 3 - Port 3.0
- */
-#define MAX_PEX 4
-static struct mvebu_pcie pcie_bus[MAX_PEX];
-
-static void mvebu_get_port_lane(struct mvebu_pcie *pcie, int pex_idx,
- int *mem_target, int *mem_attr)
-{
- u8 port[] = { 0, 1, 2, 3 };
- u8 lane[] = { 0, 0, 0, 0 };
- u8 target[] = { 8, 4, 4, 4 };
- u8 attr[] = { 0xe8, 0xe8, 0xd8, 0xb8 };
-
- pcie->port = port[pex_idx];
- pcie->lane = lane[pex_idx];
- *mem_target = target[pex_idx];
- *mem_attr = attr[pex_idx];
-}
-#else
-#define PCIE_BASE(if) \
- ((if) < 8 ? \
- (MVEBU_REG_PCIE_BASE + ((if) / 4) * 0x40000 + ((if) % 4) * 0x4000) : \
- (MVEBU_REG_PCIE_BASE + 0x2000 + ((if) % 8) * 0x40000))
-
-/*
- * On AXP MV78460 these PEX ports are supported:
- * 0 - Port 0.0
- * 1 - Port 0.1
- * 2 - Port 0.2
- * 3 - Port 0.3
- * 4 - Port 1.0
- * 5 - Port 1.1
- * 6 - Port 1.2
- * 7 - Port 1.3
- * 8 - Port 2.0
- * 9 - Port 3.0
- */
-#define MAX_PEX 10
-static struct mvebu_pcie pcie_bus[MAX_PEX];
-
-static void mvebu_get_port_lane(struct mvebu_pcie *pcie, int pex_idx,
- int *mem_target, int *mem_attr)
-{
- u8 port[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 3 };
- u8 lane[] = { 0, 1, 2, 3, 0, 1, 2, 3, 0, 0 };
- u8 target[] = { 4, 4, 4, 4, 8, 8, 8, 8, 4, 8 };
- u8 attr[] = { 0xe8, 0xd8, 0xb8, 0x78,
- 0xe8, 0xd8, 0xb8, 0x78,
- 0xf8, 0xf8 };
-
- pcie->port = port[pex_idx];
- pcie->lane = lane[pex_idx];
- *mem_target = target[pex_idx];
- *mem_attr = attr[pex_idx];
-}
-#endif
-
-static int mvebu_pex_unit_is_x4(int pex_idx)
-{
- int pex_unit = pex_idx < 9 ? pex_idx >> 2 : 3;
- u32 mask = (0x0f << (pex_unit * 8));
-
- return (readl(COMPHY_REFCLK_ALIGNMENT) & mask) == mask;
-}
-
static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie)
{
u32 val;
@@ -211,67 +136,83 @@
return container_of(hose, struct mvebu_pcie, hose);
}
-static int mvebu_pcie_read_config_dword(struct pci_controller *hose,
- pci_dev_t dev, int offset, u32 *val)
+static int mvebu_pcie_read_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
{
- struct mvebu_pcie *pcie = hose_to_pcie(hose);
+ struct mvebu_pcie *pcie = dev_get_platdata(bus);
int local_bus = PCI_BUS(pcie->dev);
int local_dev = PCI_DEV(pcie->dev);
u32 reg;
+ u32 data;
+
+ debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
/* Only allow one other device besides the local one on the local bus */
- if (PCI_BUS(dev) == local_bus && PCI_DEV(dev) != local_dev) {
- if (local_dev == 0 && PCI_DEV(dev) != 1) {
+ if (PCI_BUS(bdf) == local_bus && PCI_DEV(bdf) != local_dev) {
+ if (local_dev == 0 && PCI_DEV(bdf) != 1) {
+ debug("- out of range\n");
/*
* If local dev is 0, the first other dev can
* only be 1
*/
- *val = 0xffffffff;
- return 1;
- } else if (local_dev != 0 && PCI_DEV(dev) != 0) {
+ *valuep = pci_get_ff(size);
+ return 0;
+ } else if (local_dev != 0 && PCI_DEV(bdf) != 0) {
+ debug("- out of range\n");
/*
* If local dev is not 0, the first other dev can
* only be 0
*/
- *val = 0xffffffff;
- return 1;
+ *valuep = pci_get_ff(size);
+ return 0;
}
}
/* write address */
- reg = PCIE_CONF_ADDR(dev, offset);
+ reg = PCIE_CONF_ADDR(bdf, offset);
writel(reg, pcie->base + PCIE_CONF_ADDR_OFF);
- *val = readl(pcie->base + PCIE_CONF_DATA_OFF);
+ data = readl(pcie->base + PCIE_CONF_DATA_OFF);
+ debug("(addr,val)=(0x%04x, 0x%08x)\n", offset, data);
+ *valuep = pci_conv_32_to_size(data, offset, size);
return 0;
}
-static int mvebu_pcie_write_config_dword(struct pci_controller *hose,
- pci_dev_t dev, int offset, u32 val)
+static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
{
- struct mvebu_pcie *pcie = hose_to_pcie(hose);
+ struct mvebu_pcie *pcie = dev_get_platdata(bus);
int local_bus = PCI_BUS(pcie->dev);
int local_dev = PCI_DEV(pcie->dev);
+ u32 data;
+
+ 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);
/* Only allow one other device besides the local one on the local bus */
- if (PCI_BUS(dev) == local_bus && PCI_DEV(dev) != local_dev) {
- if (local_dev == 0 && PCI_DEV(dev) != 1) {
+ if (PCI_BUS(bdf) == local_bus && PCI_DEV(bdf) != local_dev) {
+ if (local_dev == 0 && PCI_DEV(bdf) != 1) {
/*
* If local dev is 0, the first other dev can
* only be 1
*/
- return 1;
- } else if (local_dev != 0 && PCI_DEV(dev) != 0) {
+ return 0;
+ } else if (local_dev != 0 && PCI_DEV(bdf) != 0) {
/*
* If local dev is not 0, the first other dev can
* only be 0
*/
- return 1;
+ return 0;
}
}
- writel(PCIE_CONF_ADDR(dev, offset), pcie->base + PCIE_CONF_ADDR_OFF);
- writel(val, pcie->base + PCIE_CONF_DATA_OFF);
+ writel(PCIE_CONF_ADDR(bdf, offset), pcie->base + PCIE_CONF_ADDR_OFF);
+ data = pci_conv_size_to_32(0, value, offset, size);
+ writel(data, pcie->base + PCIE_CONF_DATA_OFF);
return 0;
}
@@ -331,107 +272,242 @@
pcie->base + PCIE_BAR_CTRL_OFF(1));
}
-void pci_init_board(void)
+static int mvebu_pcie_probe(struct udevice *dev)
{
- int mem_target, mem_attr, i;
- int bus = 0;
+ struct mvebu_pcie *pcie = dev_get_platdata(dev);
+ struct udevice *ctlr = pci_get_controller(dev);
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ static int bus;
u32 reg;
- u32 soc_ctrl = readl(MVEBU_SYSTEM_REG_BASE + 0x4);
- /* Check SoC Control Power State */
- debug("%s: SoC Control %08x, 0en %01lx, 1en %01lx, 2en %01lx\n",
- __func__, soc_ctrl, SELECT(soc_ctrl, 0), SELECT(soc_ctrl, 1),
- SELECT(soc_ctrl, 2));
+ debug("%s: PCIe %d.%d - up, base %08x\n", __func__,
+ pcie->port, pcie->lane, (u32)pcie->base);
- for (i = 0; i < MAX_PEX; i++) {
- struct mvebu_pcie *pcie = &pcie_bus[i];
- struct pci_controller *hose = &pcie->hose;
+ /* Read Id info and local bus/dev */
+ debug("direct conf read %08x, local bus %d, local dev %d\n",
+ readl(pcie->base), mvebu_pcie_get_local_bus_nr(pcie),
+ mvebu_pcie_get_local_dev_nr(pcie));
- /* Get port number, lane number and memory target / attr */
- mvebu_get_port_lane(pcie, i, &mem_target, &mem_attr);
+ mvebu_pcie_set_local_bus_nr(pcie, bus);
+ mvebu_pcie_set_local_dev_nr(pcie, 0);
+ pcie->dev = PCI_BDF(bus, 0, 0);
- /* Don't read at all from pci registers if port power is down */
- if (SELECT(soc_ctrl, pcie->port) == 0) {
- if (pcie->lane == 0)
- debug("%s: skipping port %d\n", __func__, pcie->port);
+ pcie->mem.start = (u32)mvebu_pcie_membase;
+ pcie->mem.end = pcie->mem.start + PCIE_MEM_SIZE - 1;
+ mvebu_pcie_membase += PCIE_MEM_SIZE;
+
+ if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr,
+ (phys_addr_t)pcie->mem.start,
+ PCIE_MEM_SIZE)) {
+ printf("PCIe unable to add mbus window for mem at %08x+%08x\n",
+ (u32)pcie->mem.start, PCIE_MEM_SIZE);
+ }
+
+ /* Setup windows and configure host bridge */
+ mvebu_pcie_setup_wins(pcie);
+
+ /* Master + slave enable. */
+ reg = readl(pcie->base + PCIE_CMD_OFF);
+ reg |= PCI_COMMAND_MEMORY;
+ reg |= PCI_COMMAND_MASTER;
+ reg |= BIT(10); /* disable interrupts */
+ writel(reg, pcie->base + PCIE_CMD_OFF);
+
+ /* Set BAR0 to internal registers */
+ writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0));
+ writel(0, pcie->base + PCIE_BAR_HI_OFF(0));
+
+ /* PCI memory space */
+ pci_set_region(hose->regions + 0, pcie->mem.start,
+ pcie->mem.start, PCIE_MEM_SIZE, PCI_REGION_MEM);
+ pci_set_region(hose->regions + 1,
+ 0, 0,
+ gd->ram_size,
+ PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+ hose->region_count = 2;
+
+ bus++;
+
+ return 0;
+}
+
+static int mvebu_pcie_port_parse_dt(ofnode node, struct mvebu_pcie *pcie)
+{
+ const u32 *addr;
+ int len;
+
+ addr = ofnode_get_property(node, "assigned-addresses", &len);
+ if (!addr) {
+ pr_err("property \"assigned-addresses\" not found");
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE);
+
+ return 0;
+}
+
+#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03)
+#define DT_TYPE_IO 0x1
+#define DT_TYPE_MEM32 0x2
+#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF)
+#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF)
+
+static int mvebu_get_tgt_attr(ofnode node, int devfn,
+ unsigned long type,
+ unsigned int *tgt,
+ unsigned int *attr)
+{
+ const int na = 3, ns = 2;
+ const __be32 *range;
+ int rlen, nranges, rangesz, pna, i;
+
+ *tgt = -1;
+ *attr = -1;
+
+ range = ofnode_get_property(node, "ranges", &rlen);
+ if (!range)
+ return -EINVAL;
+
+ pna = 2; /* hardcoded for now because of lack of of_n_addr_cells() */
+ rangesz = pna + na + ns;
+ nranges = rlen / sizeof(__be32) / rangesz;
+
+ for (i = 0; i < nranges; i++, range += rangesz) {
+ u32 flags = of_read_number(range, 1);
+ u32 slot = of_read_number(range + 1, 1);
+ u64 cpuaddr = of_read_number(range + na, pna);
+ unsigned long rtype;
+
+ if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO)
+ rtype = IORESOURCE_IO;
+ else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
+ rtype = IORESOURCE_MEM;
+ else
continue;
- }
- pcie->base = (void __iomem *)PCIE_BASE(i);
-
- /* Check link and skip ports that have no link */
- if (!mvebu_pcie_link_up(pcie)) {
- debug("%s: PCIe %d.%d - down\n", __func__,
- pcie->port, pcie->lane);
- continue;
- }
- debug("%s: PCIe %d.%d - up, base %08x\n", __func__,
- pcie->port, pcie->lane, (u32)pcie->base);
-
- /* Read Id info and local bus/dev */
- debug("direct conf read %08x, local bus %d, local dev %d\n",
- readl(pcie->base), mvebu_pcie_get_local_bus_nr(pcie),
- mvebu_pcie_get_local_dev_nr(pcie));
-
- mvebu_pcie_set_local_bus_nr(pcie, bus);
- mvebu_pcie_set_local_dev_nr(pcie, 0);
- pcie->dev = PCI_BDF(bus, 0, 0);
-
- pcie->mem.start = (u32)mvebu_pcie_membase;
- pcie->mem.end = pcie->mem.start + PCIE_MEM_SIZE - 1;
- mvebu_pcie_membase += PCIE_MEM_SIZE;
-
- if (mvebu_mbus_add_window_by_id(mem_target, mem_attr,
- (phys_addr_t)pcie->mem.start,
- PCIE_MEM_SIZE)) {
- printf("PCIe unable to add mbus window for mem at %08x+%08x\n",
- (u32)pcie->mem.start, PCIE_MEM_SIZE);
- }
-
- /* Setup windows and configure host bridge */
- mvebu_pcie_setup_wins(pcie);
-
- /* Master + slave enable. */
- reg = readl(pcie->base + PCIE_CMD_OFF);
- reg |= PCI_COMMAND_MEMORY;
- reg |= PCI_COMMAND_MASTER;
- reg |= BIT(10); /* disable interrupts */
- writel(reg, pcie->base + PCIE_CMD_OFF);
-
- /* Setup U-Boot PCI Controller */
- hose->first_busno = 0;
- hose->current_busno = bus;
-
- /* PCI memory space */
- pci_set_region(hose->regions + 0, pcie->mem.start,
- pcie->mem.start, PCIE_MEM_SIZE, PCI_REGION_MEM);
- pci_set_region(hose->regions + 1,
- 0, 0,
- gd->ram_size,
- PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
- hose->region_count = 2;
-
- pci_set_ops(hose,
- pci_hose_read_config_byte_via_dword,
- pci_hose_read_config_word_via_dword,
- mvebu_pcie_read_config_dword,
- pci_hose_write_config_byte_via_dword,
- pci_hose_write_config_word_via_dword,
- mvebu_pcie_write_config_dword);
- pci_register_hose(hose);
-
- hose->last_busno = pci_hose_scan(hose);
-
- /* Set BAR0 to internal registers */
- writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0));
- writel(0, pcie->base + PCIE_BAR_HI_OFF(0));
-
- bus = hose->last_busno + 1;
-
- /* need to skip more for X4 links, otherwise scan will hang */
- if (mvebu_soc_family() == MVEBU_SOC_AXP) {
- if (mvebu_pex_unit_is_x4(i))
- i += 3;
+ /*
+ * The Linux code used PCI_SLOT() here, which expects devfn
+ * in bits 7..0. PCI_DEV() in U-Boot is similar to PCI_SLOT(),
+ * only expects devfn in 15..8, where its saved in this driver.
+ */
+ if (slot == PCI_DEV(devfn) && type == rtype) {
+ *tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
+ *attr = DT_CPUADDR_TO_ATTR(cpuaddr);
+ return 0;
}
}
+
+ return -ENOENT;
}
+
+static int mvebu_pcie_ofdata_to_platdata(struct udevice *dev)
+{
+ struct mvebu_pcie *pcie = dev_get_platdata(dev);
+ int ret = 0;
+
+ /* Get port number, lane number and memory target / attr */
+ if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port",
+ &pcie->port)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-lane", &pcie->lane))
+ pcie->lane = 0;
+
+ sprintf(pcie->name, "pcie%d.%d", pcie->port, pcie->lane);
+
+ /* pci_get_devfn() returns devfn in bits 15..8, see PCI_DEV usage */
+ pcie->devfn = pci_get_devfn(dev);
+ if (pcie->devfn < 0) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn,
+ IORESOURCE_MEM,
+ &pcie->mem_target, &pcie->mem_attr);
+ if (ret < 0) {
+ printf("%s: cannot get tgt/attr for mem window\n", pcie->name);
+ goto err;
+ }
+
+ /* Parse PCIe controller register base from DT */
+ ret = mvebu_pcie_port_parse_dt(dev_ofnode(dev), pcie);
+ if (ret < 0)
+ goto err;
+
+ /* Check link and skip ports that have no link */
+ if (!mvebu_pcie_link_up(pcie)) {
+ debug("%s: %s - down\n", __func__, pcie->name);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static const struct dm_pci_ops mvebu_pcie_ops = {
+ .read_config = mvebu_pcie_read_config,
+ .write_config = mvebu_pcie_write_config,
+};
+
+static struct driver pcie_mvebu_drv = {
+ .name = "pcie_mvebu",
+ .id = UCLASS_PCI,
+ .ops = &mvebu_pcie_ops,
+ .probe = mvebu_pcie_probe,
+ .ofdata_to_platdata = mvebu_pcie_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct mvebu_pcie),
+};
+
+/*
+ * Use a MISC device to bind the n instances (child nodes) of the
+ * PCIe base controller in UCLASS_PCI.
+ */
+static int mvebu_pcie_bind(struct udevice *parent)
+{
+ struct mvebu_pcie *pcie;
+ struct uclass_driver *drv;
+ struct udevice *dev;
+ ofnode subnode;
+
+ /* Lookup eth driver */
+ drv = lists_uclass_lookup(UCLASS_PCI);
+ if (!drv) {
+ puts("Cannot find PCI driver\n");
+ return -ENOENT;
+ }
+
+ ofnode_for_each_subnode(subnode, dev_ofnode(parent)) {
+ if (!ofnode_is_available(subnode))
+ continue;
+
+ pcie = calloc(1, sizeof(*pcie));
+ if (!pcie)
+ return -ENOMEM;
+
+ /* Create child device UCLASS_PCI and bind it */
+ device_bind_ofnode(parent, &pcie_mvebu_drv, pcie->name, pcie,
+ subnode, &dev);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id mvebu_pcie_ids[] = {
+ { .compatible = "marvell,armada-xp-pcie" },
+ { .compatible = "marvell,armada-370-pcie" },
+ { }
+};
+
+U_BOOT_DRIVER(pcie_mvebu_base) = {
+ .name = "pcie_mvebu_base",
+ .id = UCLASS_MISC,
+ .of_match = mvebu_pcie_ids,
+ .bind = mvebu_pcie_bind,
+};