Raspberry PI 5 has RP1 chip connected over PCIE and working as a
bridge to different hardware.
This driver implementation was inspired by MFD RP1 driver in the Linux
Kernel.

Signed-off-by: Oleksii Moisieiev <oleksii_moisie...@epam.com>
Reviewed-by: Volodymyr Babchuk <volodymyr_babc...@epam.com>
---

 drivers/mfd/Kconfig  |  10 ++++
 drivers/mfd/Makefile |   1 +
 drivers/mfd/rp1.c    | 120 +++++++++++++++++++++++++++++++++++++++++++
 include/pci_ids.h    |   3 ++
 4 files changed, 134 insertions(+)
 create mode 100644 drivers/mfd/rp1.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ae53b02f27..a9de45797f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2,3 +2,13 @@ config MFD_ATMEL_SMC
        bool "Atmel Static Memory Controller driver"
        help
        Say yes here to support Atmel Static Memory Controller driver.
+
+config MFD_RP1
+       tristate "RP1 MFD driver"
+       depends on PCI
+       help
+         Support for the RP1 peripheral chip.
+
+         This driver provides support for the Raspberry Pi RP1 peripheral chip.
+         It is responsible for enabling the Device Tree node once the PCIe 
endpoint
+         has been configureds.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 4454815a98..c81ee5789b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
+obj-$(CONFIG_MFD_RP1) += rp1.o
diff --git a/drivers/mfd/rp1.c b/drivers/mfd/rp1.c
new file mode 100644
index 0000000000..265ed668f5
--- /dev/null
+++ b/drivers/mfd/rp1.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 EPAM Systems
+ *
+ * Derived from linux rp1 driver
+ * Copyright (c) 2018-22 Raspberry Pi Ltd.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/of_access.h>
+#include <dt-bindings/mfd/rp1.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <pci.h>
+
+#define RP1_B0_CHIP_ID 0x10001927
+#define RP1_C0_CHIP_ID 0x20001927
+
+#define RP1_PLATFORM_ASIC BIT(1)
+#define RP1_PLATFORM_FPGA BIT(0)
+
+#define RP1_DRIVER_NAME "rp1"
+
+#define PCI_DEVICE_REV_RP1_C0 2
+
+#define SYSINFO_CHIP_ID_OFFSET 0x00000000
+#define SYSINFO_PLATFORM_OFFSET        0x00000004
+
+struct rp1_dev {
+       phys_addr_t bar_start;
+};
+
+static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int 
offset)
+{
+       return rp1->bar_start + offset;
+}
+
+static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 
offset)
+{
+       resource_size_t phys = rp1_io_to_phys(rp1, base_addr);
+       void __iomem *regblock = ioremap(phys, 0x1000);
+       u32 value = readl(regblock + offset);
+
+       iounmap(regblock);
+       return value;
+}
+
+static int rp1_get_bar_region(struct udevice *dev, phys_addr_t *bar_start)
+{
+       *bar_start = (phys_addr_t)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, 0, 0,
+                       PCI_REGION_TYPE, PCI_REGION_MEM);
+       return 0;
+}
+
+static int rp1_probe(struct udevice *dev)
+{
+       int ret;
+       struct rp1_dev *rp1 = dev_get_priv(dev);
+       u32 chip_id, platform;
+
+       /* Turn on bus-mastering */
+       dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MASTER | 
PCI_COMMAND_MEMORY);
+
+       ret = rp1_get_bar_region(dev, &rp1->bar_start);
+       if (ret)
+               return ret;
+
+       /* Get chip id */
+       chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET);
+       platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET);
+       dev_dbg(dev, "chip_id 0x%x%s\n", chip_id,
+               (platform & RP1_PLATFORM_FPGA) ? " FPGA" : "");
+
+       if (chip_id != RP1_C0_CHIP_ID) {
+               dev_err(dev, "wrong chip id (%x)\n", chip_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rp1_bind(struct udevice *dev)
+{
+       device_set_name(dev, RP1_DRIVER_NAME);
+       return 0;
+}
+
+static const struct pci_device_id dev_id_table[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), },
+       { 0, }
+};
+
+static int rp1_pcie_read_config(const struct udevice *bus, pci_dev_t bdf,
+                               uint offset, ulong *valuep, enum pci_size_t 
size)
+{
+       /*
+        * Leaving this call because pci subsystem calls for read_config
+        * and produces error then this callback is not set.
+        * Just return 0 here.s
+        */
+       *valuep = 0;
+       return 0;
+}
+
+static const struct dm_pci_ops rp1_pcie_ops = {
+       .read_config    = rp1_pcie_read_config,
+};
+
+U_BOOT_DRIVER(rp1_driver) = {
+       .name                   = RP1_DRIVER_NAME,
+       .id                     = UCLASS_PCI_GENERIC,
+       .probe                  = rp1_probe,
+       .bind                   = rp1_bind,
+       .priv_auto              = sizeof(struct rp1_dev),
+       .ops                    = &rp1_pcie_ops,
+};
+
+U_BOOT_PCI_DEVICE(rp1_driver, dev_id_table);
diff --git a/include/pci_ids.h b/include/pci_ids.h
index f1886c3a75..4027b89b25 100644
--- a/include/pci_ids.h
+++ b/include/pci_ids.h
@@ -2556,6 +2556,9 @@
 #define PCI_VENDOR_ID_TEKRAM           0x1de1
 #define PCI_DEVICE_ID_TEKRAM_DC290     0xdc29
 
+#define PCI_VENDOR_ID_RPI              0x1de4
+#define PCI_DEVICE_ID_RP1_C0           0x0001
+
 #define PCI_VENDOR_ID_TEHUTI           0x1fc9
 #define PCI_DEVICE_ID_TEHUTI_3009      0x3009
 #define PCI_DEVICE_ID_TEHUTI_3010      0x3010
-- 
2.34.1

Reply via email to