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.jingf...@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

Reply via email to