[PATCH v2] pci: add of_pci_bridge_init()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



So far we assume a 1:1 mapping between the CPU and bus address space.
This is not always the case and different mappings are described in the
ranges device tree property. Parse the property and call
pci_add_resource_offset() accordingly. The code is based on the
corresponding Kernel code.

Link: https://lore.barebox.org/20240326100746.471532-13-s.hauer@xxxxxxxxxxxxxx
Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
---

Changes since v1:
- add a commit message
- mips registers PCI controllers without a parent device. Do not crash in this case

 drivers/pci/Makefile |   1 +
 drivers/pci/of.c     | 102 +++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.c    |   2 +
 include/linux/pci.h  |  10 +++++
 4 files changed, 115 insertions(+)
 create mode 100644 drivers/pci/of.c

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index c222f77b3b..69649fbcd2 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the PCI bus specific drivers.
 #
 obj-y		+= pci.o bus.o pci_iomap.o host-bridge.o
+obj-$(CONFIG_OFDEVICE) += of.o
 
 ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
 
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
new file mode 100644
index 0000000000..1cf7fe302d
--- /dev/null
+++ b/drivers/pci/of.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PCI <-> OF mapping helpers
+ *
+ * Copyright 2011 IBM Corp.
+ */
+#define pr_fmt(fmt)	"PCI: OF: " fmt
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <of.h>
+#include <of_address.h>
+#include <common.h>
+#include <linux/resource_ext.h>
+
+/**
+ * of_pci_get_host_bridge_resources() - parsing of PCI host bridge resources from DT
+ * @dev: host bridge device
+ * @resources: list where the range of resources will be added after DT parsing
+ *
+ * This function will parse the "ranges" property of a PCI host bridge device
+ * node and setup the resource mapping based on its content. It is expected
+ * that the property conforms with the Power ePAPR document.
+ *
+ * It returns zero if the range parsing has been successful or a standard error
+ * value if it failed.
+ */
+static int of_pci_get_host_bridge_resources(struct device *dev,
+					    struct pci_controller *bridge)
+{
+	struct list_head *resources = &bridge->windows;
+	struct device_node *dev_node = dev->of_node;
+	struct resource *res, tmp_res;
+	struct of_pci_range range;
+	struct of_pci_range_parser parser;
+	const char *range_type;
+	int err;
+
+	dev_dbg(dev, "host bridge %pOF ranges:\n", dev_node);
+
+	/* Check for ranges property */
+	err = of_pci_range_parser_init(&parser, dev_node);
+	if (err)
+		return 0;
+
+	dev_dbg(dev, "Parsing ranges property...\n");
+	for_each_of_pci_range(&parser, &range) {
+		/* Read next ranges element */
+		if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+			range_type = "IO";
+		else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+			range_type = "MEM";
+		else
+			range_type = "err";
+		dev_dbg(dev, "  %6s %#012llx..%#012llx -> %#012llx\n",
+			 range_type, range.cpu_addr,
+			 range.cpu_addr + range.size - 1, range.pci_addr);
+
+		/*
+		 * If we failed translation or got a zero-sized region
+		 * then skip this range
+		 */
+		if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
+			continue;
+
+		of_pci_range_to_resource(&range, dev_node, &tmp_res);
+
+		res = kmemdup(&tmp_res, sizeof(tmp_res), GFP_KERNEL);
+		if (!res) {
+			err = -ENOMEM;
+			goto failed;
+		}
+
+		pci_add_resource_offset(resources, res,	res->start - range.pci_addr);
+
+		switch (res->flags & IORESOURCE_TYPE_BITS) {
+		case IORESOURCE_IO:
+			bridge->io_resource = res;
+			break;
+
+		case IORESOURCE_MEM:
+			if (res->flags & IORESOURCE_PREFETCH)
+				bridge->mem_pref_resource = res;
+			else
+				bridge->mem_resource = res;
+			break;
+		}
+	}
+
+	return 0;
+
+failed:
+	return err;
+}
+
+int of_pci_bridge_init(struct device *dev, struct pci_controller *bridge)
+{
+	if (!dev || !dev->of_node)
+		return 0;
+
+	return of_pci_get_host_bridge_resources(dev, bridge);
+}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 0a238cd190..bc083270d9 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -45,6 +45,8 @@ static void pci_bus_register_devices(struct pci_bus *bus)
 void pci_controller_init(struct pci_controller *hose)
 {
 	INIT_LIST_HEAD(&hose->windows);
+
+	of_pci_bridge_init(hose->parent, hose);
 }
 
 void register_pci_controller(struct pci_controller *hose)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 11a60f66a9..f6511e0095 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -414,4 +414,14 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
 void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
 			     struct pci_bus_region *region);
 
+/* drivers/pci/of.c */
+#ifdef CONFIG_OFDEVICE
+int of_pci_bridge_init(struct device *dev, struct pci_controller *bridge);
+#else
+static inline int of_pci_bridge_init(struct device *dev, struct pci_controller *bridge)
+{
+	return 0;
+}
+#endif
+
 #endif /* LINUX_PCI_H */
-- 
2.39.2





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux