ARM: orion5x: add DT support for PCI host

The PCI host controller driver is now abstracted enough that
we can simply add a new probe function that performs the DT
parsing and fills out the internal data structures.

This still needs a binding document.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c
index 84099f8..02826bc 100644
--- a/arch/arm/mach-orion5x/pci.c
+++ b/arch/arm/mach-orion5x/pci.c
@@ -10,6 +10,8 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/platform_data/pci-orion.h>
 #include <linux/platform_device.h>
@@ -342,6 +344,78 @@
 	return 1;
 }
 
+static int orion5x_pci_probe_dt(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct orion5x_pci *priv = devm_kzalloc(dev, sizeof(*priv),
+						     GFP_KERNEL);
+	struct resource *reg_phys;
+	void *hw_priv[1] = { priv };
+	/*
+	 * we always use domain 1 here and domain 0 for pcie, but after
+	 * "arm: pcibios: remove pci_sys_data domain" is merged, it will
+	 * work automatically
+	 */
+	struct hw_pci hwpci = {
+		.domain		= 1,
+		.nr_controllers	= ARRAY_SIZE(hw_priv),
+		.ops		= &orion5x_pci_ops,
+		.setup		= orion5x_pci_setup,
+		.map_irq	= of_irq_parse_and_map_pci,
+		.private_data	= hw_priv,
+	};
+	struct of_pci_range range;
+	struct of_pci_range_parser parser;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	if (!priv)
+		return -ENOMEM;
+
+	reg_phys = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!reg_phys) {
+		dev_err(dev, "missing reg property\n");
+		return -ENXIO;
+	}
+	priv->reg_phys = *reg_phys;
+
+	ret = of_pci_parse_bus_range(np, &priv->bus);
+	if (ret) {
+		dev_err(dev, "missing bus-range property\n");
+		return ret;
+	}
+
+	if (of_pci_range_parser_init(&parser, np)) {
+		dev_err(dev, "missing ranges property\n");
+		return -ENXIO;
+	}
+
+	priv->cardbus = of_property_read_bool(np, "cardbus");
+
+	for_each_of_pci_range(&parser, &range) {
+		switch (range.flags & IORESOURCE_TYPE_BITS) {
+		case IORESOURCE_IO:
+			of_pci_range_to_resource(&range, np, &priv->io_io);
+			priv->io_phys = (struct resource) {
+				.start	= range.cpu_addr,
+				.end	= range.cpu_addr + range.size - 1,
+				.name	= np->full_name,
+				.flags	= IORESOURCE_MEM,
+			};
+			break;
+
+		case IORESOURCE_MEM:
+			of_pci_range_to_resource(&range, np, &priv->mem);
+			break;
+		}
+	}
+
+	pci_common_init_dev(dev, &hwpci);
+
+	return 0;
+}
+
+
 /* hardcoded MMIO locations when booting without DT */
 #define __ORION5X_PCI_REG_PHYS_BASE	(0xf1000000 + 0x30000)
 #define __ORION5X_PCI_REG_SIZE		0x10000
@@ -349,7 +423,7 @@
 #define __ORION5X_PCI_IO_SIZE		SZ_64K
 #define __ORION5X_PCI_MEM_PHYS_BASE	0xe8000000
 #define __ORION5X_PCI_MEM_SIZE		SZ_128M
-static int orion5x_pci_probe(struct platform_device *pdev)
+static int orion5x_pci_probe_pdata(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct orion5x_pci *priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -400,9 +474,25 @@
 	return 0;
 }
 
+static int orion5x_pci_probe(struct platform_device *pdev)
+{
+	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
+		return orion5x_pci_probe_dt(pdev);
+	else if (IS_ENABLED(CONFIG_ATAGS) && dev_get_platdata(&pdev->dev))
+		return orion5x_pci_probe_pdata(pdev);
+	return -ENXIO;
+}
+
+static const struct of_device_id orion5x_pci_of_match_table[] = {
+	{ .compatible = "marvell,orion-pci", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, orion5x_pci_of_match_table);
+
 static struct platform_driver orion5x_pci_driver = {
 	.driver = {
 		.name = "orion-pci",
+		.of_match_table = orion5x_pci_of_match_table,
 		.suppress_bind_attrs = true,
 	},
 	.probe = orion5x_pci_probe,