[PATCH v15 14/19] drm/etnaviv: Add PCIe IP setup code

Sui Jingfeng sui.jingfeng at linux.dev
Sun Sep 8 09:43:52 UTC 2024


Because some PCIe IP need special setup before its VRAM bar can be usable,
do this with instance specific object function.

Signed-off-by: Sui Jingfeng <sui.jingfeng at linux.dev>
---
 drivers/gpu/drm/etnaviv/Makefile          |   3 +-
 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c |  19 ++++
 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h |   9 ++
 drivers/gpu/drm/etnaviv/pcie_ip_setup.c   | 109 ++++++++++++++++++++++
 4 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/etnaviv/pcie_ip_setup.c

diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile
index 6829e1ebf2db..383f181bfc4c 100644
--- a/drivers/gpu/drm/etnaviv/Makefile
+++ b/drivers/gpu/drm/etnaviv/Makefile
@@ -16,6 +16,7 @@ etnaviv-y := \
 	etnaviv_perfmon.o \
 	etnaviv_sched.o
 
-etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o
+etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o \
+					    pcie_ip_setup.o
 
 obj-$(CONFIG_DRM_ETNAVIV)	+= etnaviv.o
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c
index f13f3208120f..9911bfdc41a9 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c
@@ -5,6 +5,11 @@
 #include "etnaviv_drv.h"
 #include "etnaviv_pci_drv.h"
 
+static const struct etnaviv_pcie_ip_funcs jemo_9xxxx_gpu_pcie_ip_funcs = {
+	.init = jemo_pcie_init,
+	.fini = NULL,
+};
+
 static const struct etnaviv_pci_gpu_data
 gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = {
 	{
@@ -18,7 +23,9 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = {
 		.mmio_bar = 1,
 		.ip_block = {{0, 0x00900000, 0x00004000, "etnaviv-gpu,3d"},},
 		.has_dedicated_vram = true,
+		.has_iatu = true,
 		.has_display = true,
+		.pcie_ip_funcs = &jemo_9xxxx_gpu_pcie_ip_funcs,
 		.market_name = "JingJia Micro JM9100",
 	},
 	{
@@ -30,7 +37,9 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = {
 		.ip_block = {{0, 0x00900000, 0x00004000, "etnaviv-gpu,3d"},
 			     {1, 0x00910000, 0x00004000, "etnaviv-gpu,3d"},},
 		.has_dedicated_vram = true,
+		.has_iatu = true,
 		.has_display = true,
+		.pcie_ip_funcs = &jemo_9xxxx_gpu_pcie_ip_funcs,
 		.market_name = "JingJia Micro JD9230P",
 	},
 	{
@@ -42,6 +51,7 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = {
 		.ip_block = {{0, 0x00040000, 0x00004000, "etnaviv-gpu,3d"},
 			     {0, 0x000C0000, 0x00004000, "etnaviv-gpu,2d"},},
 		.has_dedicated_vram = true,
+		.has_iatu = false,
 		.has_display = true,
 		.market_name = "LingJiu GP102",
 	},
@@ -53,6 +63,7 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = {
 		.mmio_bar = 0,
 		.ip_block = {{0, 0, 0x00004000, "etnaviv-gpu,3d"},},
 		.has_dedicated_vram = true,
+		.has_iatu = false,
 		.has_display = false,
 		.market_name = "GC1000 in LS7A1000",
 	},
@@ -83,6 +94,7 @@ static int etnaviv_pci_probe(struct pci_dev *pdev,
 			     const struct pci_device_id *ent)
 {
 	const struct etnaviv_pci_gpu_data *pdata;
+	const struct etnaviv_pcie_ip_funcs *pcie_ip_funcs;
 	struct device *dev = &pdev->dev;
 	unsigned int i;
 	unsigned int num_core;
@@ -102,6 +114,13 @@ static int etnaviv_pci_probe(struct pci_dev *pdev,
 	if (!pdata)
 		return -ENODEV;
 
+	pcie_ip_funcs = pdata->pcie_ip_funcs;
+	if (pcie_ip_funcs) {
+		ret = pcie_ip_funcs->init(pdev);
+		if (ret)
+			return ret;
+	}
+
 	num_core = pdata->num_core;
 
 	dev_info(dev, "%s has %u GPU cores\n", pdata->market_name, num_core);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h
index eae8cdea5674..39eb2851355a 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h
+++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h
@@ -23,6 +23,11 @@ struct vivante_gc_ip_block {
 	char compatible[20];
 };
 
+struct etnaviv_pcie_ip_funcs {
+	int (*init)(struct pci_dev *pdev);
+	void (*fini)(struct pci_dev *pdev);
+};
+
 struct etnaviv_pci_gpu_data {
 	enum etnaviv_pci_chip_id chip_id;
 	u32 num_core;
@@ -31,13 +36,17 @@ struct etnaviv_pci_gpu_data {
 	u32 mmio_bar;
 	struct vivante_gc_ip_block ip_block[ETNA_MAX_IP_BLOCK];
 	bool has_dedicated_vram;
+	bool has_iatu;
 	bool has_display;
+	const struct etnaviv_pcie_ip_funcs *pcie_ip_funcs;
 	char market_name[24];
 };
 
 int etnaviv_register_pci_driver(void);
 void etnaviv_unregister_pci_driver(void);
 
+int jemo_pcie_init(struct pci_dev *pdev);
+
 #else
 
 static inline int etnaviv_register_pci_driver(void) { return 0; }
diff --git a/drivers/gpu/drm/etnaviv/pcie_ip_setup.c b/drivers/gpu/drm/etnaviv/pcie_ip_setup.c
new file mode 100644
index 000000000000..f90db8260c35
--- /dev/null
+++ b/drivers/gpu/drm/etnaviv/pcie_ip_setup.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/pci.h>
+
+#include "etnaviv_drv.h"
+#include "etnaviv_pci_drv.h"
+
+#define PCIE_IATU_BASE_ADDR               0x10000
+#define PCIE_IATU_BAR_ADDR_INC            0x200
+
+#define PCIE_REGION_INBOUND               1
+#define PCIE_REGION_OUTBOUND              0
+#define PCIE_REGION_DIRECT_BIT            31
+#define PCIE_REGION_DIRECT_BITMASK        0x80000000
+#define PCIE_REGION_INDEX_BITMASK         0x7FFFFFFF
+
+#define PCIE_REGION_TYPE_MEM              0x00
+#define PCIE_REGION_TYPE_IO               0x10
+
+#define PCIE_REGION_MATCH_BAR             1
+#define PCIE_REGION_MATCH_ADDR            0
+
+#define PCIE_REGION_ENABLE_BITMASK        BIT(31)
+#define PCIE_REGION_ENABLE_BIT            BIT(31)
+#define PCIE_REGION_MODE_BITMASK          BIT(30)
+#define PCIE_REGION_MODE_BIT              BIT(30)
+
+#define PCIE_REGION_BAR_NUM_BITMASK       GENMASK(10, 8)
+#define PCIE_REGION_BAR_NUM_SHIFT         8
+
+#define PCIE_REGION_INBOUND_TYPE          0x100
+#define PCIE_REGION_INBOUND_CTRL          0x104
+#define PCIE_REGION_INBOUND_ADDR_LO       0x114
+#define PCIE_REGION_INBOUND_ADDR_HI       0x118
+
+static void iatu_write(void __iomem *iatu, u32 bar, u32 offset, u32 value)
+{
+	u32 bar_base = PCIE_IATU_BASE_ADDR + bar * PCIE_IATU_BAR_ADDR_INC;
+
+	writel(value, iatu + bar_base + offset);
+}
+
+static u32 iatu_read(void __iomem *iatu, u32 bar, u32 offset)
+{
+	u32 bar_base = PCIE_IATU_BASE_ADDR + bar * PCIE_IATU_BAR_ADDR_INC;
+
+	return readl(iatu + bar_base + offset);
+}
+
+static int iatu_map_bar(void __iomem *iatu, u32 bar, u64 axi_addr)
+{
+	u32 addr_hi = axi_addr >> 32;
+	u32 addr_lo = axi_addr & 0xffffffff;
+	u32 val;
+
+	iatu_write(iatu, bar + 9, PCIE_REGION_INBOUND_ADDR_LO, addr_lo);
+	iatu_write(iatu, bar + 9, PCIE_REGION_INBOUND_ADDR_HI, addr_hi);
+	iatu_write(iatu, bar + 9, PCIE_REGION_INBOUND_TYPE,
+				  PCIE_REGION_TYPE_MEM);
+
+	val = PCIE_REGION_ENABLE_BIT |
+	      PCIE_REGION_MODE_BIT |
+	      bar << PCIE_REGION_BAR_NUM_SHIFT;
+	iatu_write(iatu, bar + 9, PCIE_REGION_INBOUND_CTRL, val);
+
+	/* sanity check */
+	val = iatu_read(iatu, bar + 9, PCIE_REGION_INBOUND_ADDR_LO);
+	if (val != addr_lo) {
+		pr_err("%s : %u\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	val = iatu_read(iatu, bar + 9, PCIE_REGION_INBOUND_ADDR_HI);
+	if (val != addr_hi) {
+		pr_err("%s : %u\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int jemo_pcie_init(struct pci_dev *pdev)
+{
+	void __iomem *iatu;
+	int ret;
+
+	/* Bar 4 is PCIe iATU */
+	iatu = pci_iomap(pdev, 4, 0);
+	if (!iatu)
+		return -ENOMEM;
+
+	ret = iatu_map_bar(iatu, 0, 0x10000000);
+	if (ret)
+		return ret;
+
+	ret = iatu_map_bar(iatu, 1, 0x00000000);
+	if (ret)
+		return ret;
+
+	ret = iatu_map_bar(iatu, 2, 0x10000000);
+	if (ret)
+		return ret;
+
+	pci_iounmap(pdev, iatu);
+
+	dev_info(&pdev->dev, "PCIe iATU init done\n");
+
+	return 0;
+}
-- 
2.43.0



More information about the dri-devel mailing list