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