From: Torsten Duwe <[email protected]>

Add a driver for the clocks in RP1, starting with ETH_TSU.
Derived from Linux drivers/clk/clk-rp1.c.

Signed-off-by: Torsten Duwe <[email protected]>
Co-authored-by: Oleksii Moisieiev <[email protected]>
---

While not strictly required in this series, this patch completes a
minimal RP1 support, as far as required for U-Boot. Network boot will
later only need adaptions of the MACB ethernet code to the AXI and
PCIe bridges.

---
 configs/rpi_arm64_defconfig   |   2 +
 drivers/clk/Kconfig           |   7 +
 drivers/clk/Makefile          |   1 +
 drivers/clk/clk-rp1.c         | 280 ++++++++++++++++++++++++++++++++++
 include/dt-bindings/clk/rp1.h |  56 +++++++
 5 files changed, 346 insertions(+)
 create mode 100644 drivers/clk/clk-rp1.c
 create mode 100644 include/dt-bindings/clk/rp1.h

diff --git a/configs/rpi_arm64_defconfig b/configs/rpi_arm64_defconfig
index fb0fdce1d7f..a4adfecfbe2 100644
--- a/configs/rpi_arm64_defconfig
+++ b/configs/rpi_arm64_defconfig
@@ -35,6 +35,8 @@ CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
 CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
 CONFIG_NET_LWIP=y
 CONFIG_DM_DMA=y
+CONFIG_CLK=y
+CONFIG_CLK_RP1=y
 CONFIG_DFU_MMC=y
 CONFIG_SYS_DFU_DATA_BUF_SIZE=0x100000
 CONFIG_SYS_DFU_MAX_FILE_SIZE=0x200000
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 47da49c8314..a7b5009a014 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -181,6 +181,13 @@ config CLK_OCTEON
        help
          Enable this to support the clocks on Octeon MIPS platforms.
 
+config CLK_RP1
+       bool "Raspberry Pi RP1-based clock support"
+       depends on PCI && CLK
+       help
+         Enable common clock framework support for Raspberry Pi RP1
+         support.
+
 config SANDBOX_CLK_CCF
        bool "Sandbox Common Clock Framework [CCF] support"
        depends on SANDBOX
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4b241a9240a..4467779fb79 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -63,3 +63,4 @@ obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
 obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
 obj-$(CONFIG_SANDBOX) += clk_sandbox.o
 obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
+obj-$(CONFIG_CLK_RP1) += clk-rp1.o
diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c
new file mode 100644
index 00000000000..94e12271345
--- /dev/null
+++ b/drivers/clk/clk-rp1.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Clock driver for RP1 PCIe multifunction chip.
+ *
+ * Copyright (C) 2024 EPAM Systems
+ *
+ * Derived from linux clk-rp1 driver
+ *   Copyright (C) 2023 Raspberry Pi Ltd.
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/math64.h>
+
+#include <dt-bindings/clk/rp1.h>
+
+#define GPCLK_OE_CTRL                  0x00000
+
+#define CLK_ETH_TSU_CTRL               0x00134
+#define CLK_ETH_TSU_DIV_INT            0x00138
+#define CLK_ETH_TSU_SEL                        0x00140
+
+#define FC_NUM(idx, off)               ((idx) * 32 + (off))
+
+#define DIV_INT_8BIT_MAX               0x000000ffu /* max divide for most 
clocks */
+
+/* Clock fields for all clocks */
+#define CLK_CTRL_ENABLE                        BIT(11)
+#define CLK_DIV_FRAC_BITS              16
+
+#define KHz                            1000
+#define MHz                            (KHz * KHz)
+
+#define MAX_CLK_PARENTS        16
+#define DIV_U64_NEAREST(a, b) div_u64(((a) + ((b) >> 1)), (b))
+
+struct rp1_clockman {
+       struct udevice *dev;
+       void __iomem *regs;
+       spinlock_t regs_lock; /* spinlock for all clocks */
+};
+
+struct rp1_pll_core_data {
+       const char *name;
+       u32 cs_reg;
+       u32 pwr_reg;
+       u32 fbdiv_int_reg;
+       u32 fbdiv_frac_reg;
+       unsigned long flags;
+       u32 fc0_src;
+};
+
+struct rp1_pll_data {
+       const char *name;
+       const char *source_pll;
+       u32 ctrl_reg;
+       unsigned long flags;
+       u32 fc0_src;
+};
+
+struct rp1_pll_ph_data {
+       const char *name;
+       const char *source_pll;
+       unsigned int phase;
+       unsigned int fixed_divider;
+       u32 ph_reg;
+       unsigned long flags;
+       u32 fc0_src;
+};
+
+struct rp1_pll_divider_data {
+       const char *name;
+       const char *source_pll;
+       u32 sec_reg;
+       unsigned long flags;
+       u32 fc0_src;
+};
+
+struct rp1_clock_data {
+       const char *name;
+       const char *const parents[MAX_CLK_PARENTS];
+       int num_std_parents;
+       int num_aux_parents;
+       unsigned long flags;
+       u32 oe_mask;
+       u32 clk_src_mask;
+       u32 ctrl_reg;
+       u32 div_int_reg;
+       u32 div_frac_reg;
+       u32 sel_reg;
+       u32 div_int_max;
+       unsigned long max_freq;
+       u32 fc0_src;
+};
+
+struct rp1_pll_core {
+       struct clk hw;
+       struct rp1_clockman *clockman;
+       const struct rp1_pll_core_data *data;
+       unsigned long cached_rate;
+};
+
+struct rp1_pll {
+       struct clk hw;
+       struct clk_divider div;
+       struct rp1_clockman *clockman;
+       const struct rp1_pll_data *data;
+       unsigned long cached_rate;
+};
+
+struct rp1_pll_ph {
+       struct clk hw;
+       struct rp1_clockman *clockman;
+       const struct rp1_pll_ph_data *data;
+};
+
+struct rp1_clock {
+       struct clk hw;
+       struct rp1_clockman *clockman;
+       const struct rp1_clock_data *data;
+       unsigned long cached_rate;
+};
+
+struct rp1_clk_change {
+       struct clk *hw;
+       unsigned long new_rate;
+};
+
+struct rp1_clk_change rp1_clk_chg_tree[3];
+
+static inline
+void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val)
+{
+       writel(val, clockman->regs + reg);
+}
+
+static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg)
+{
+       return readl(clockman->regs + reg);
+}
+
+static struct rp1_clock_data rp1_data[] = {
+[RP1_CLK_ETH_TSU] = {
+                       .name = "clk_eth_tsu",
+                       .parents = {"xosc",
+                                   "pll_video_sec",
+                                   "clksrc_gp0",
+                                   "clksrc_gp1",
+                                   "clksrc_gp2",
+                                   "clksrc_gp3",
+                                   "clksrc_gp4",
+                                   "clksrc_gp5"},
+                       .num_std_parents = 0,
+                       .num_aux_parents = 8,
+                       .ctrl_reg = CLK_ETH_TSU_CTRL,
+                       .div_int_reg = CLK_ETH_TSU_DIV_INT,
+                       .div_frac_reg = 0,
+                       .sel_reg = CLK_ETH_TSU_SEL,
+                       .div_int_max = DIV_INT_8BIT_MAX,
+                       .max_freq = 50 * MHz,
+                       .fc0_src = FC_NUM(5, 7),
+       },
+};
+
+static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate,
+                               const struct rp1_clock_data *data)
+{
+       u64 div;
+
+       /*
+        * Due to earlier rounding, calculated parent_rate may differ from
+        * expected value. Don't fail on a small discrepancy near unity divide.
+        */
+       if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS))
+               return 0;
+
+       /*
+        * Always express div in fixed-point format for fractional division;
+        * If no fractional divider is present, the fraction part will be zero.
+        */
+       if (data->div_frac_reg) {
+               div = (u64)parent_rate << CLK_DIV_FRAC_BITS;
+               div = DIV_U64_NEAREST(div, rate);
+       } else {
+               div = DIV_U64_NEAREST(parent_rate, rate);
+               div <<= CLK_DIV_FRAC_BITS;
+       }
+
+       div = clamp(div,
+                   1ull << CLK_DIV_FRAC_BITS,
+                   (u64)data->div_int_max << CLK_DIV_FRAC_BITS);
+
+       return div;
+}
+
+static ulong rp1_clock_set_rate(struct clk *hw, unsigned long rate)
+{
+       struct rp1_clockman *clockman = dev_get_priv(hw->dev);
+       const struct rp1_clock_data *data = &rp1_data[hw->id];
+       u32 div = rp1_clock_choose_div(rate, 0x2faf080, data);
+
+       if (hw->id != RP1_CLK_ETH_TSU)
+               return 0;
+
+       WARN(rate > 4000000000ll, "rate is -ve (%d)\n", (int)rate);
+
+       if (WARN(!div,
+                "clk divider calculated as 0! (%s, rate %ld, parent rate 
%d)\n",
+                data->name, rate, 0x2faf080))
+               div = 1 << CLK_DIV_FRAC_BITS;
+
+       spin_lock(&clockman->regs_lock);
+
+       clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS);
+       if (data->div_frac_reg)
+               clockman_write(clockman, data->div_frac_reg, div << (32 - 
CLK_DIV_FRAC_BITS));
+
+       spin_unlock(&clockman->regs_lock);
+
+       return 0;
+}
+
+static int rp1_clock_on(struct clk *hw)
+{
+       const struct rp1_clock_data *data = &rp1_data[hw->id];
+       struct rp1_clockman *clockman = dev_get_priv(hw->dev);
+
+       if (hw->id != RP1_CLK_ETH_TSU)
+               return 0;
+
+       spin_lock(&clockman->regs_lock);
+       clockman_write(clockman, data->ctrl_reg,
+                      clockman_read(clockman, data->ctrl_reg) | 
CLK_CTRL_ENABLE);
+       /* If this is a GPCLK, turn on the output-enable */
+       if (data->oe_mask)
+               clockman_write(clockman, GPCLK_OE_CTRL,
+                              clockman_read(clockman, GPCLK_OE_CTRL) | 
data->oe_mask);
+
+       spin_unlock(&clockman->regs_lock);
+
+       return 0;
+}
+
+static int rp1_clk_probe(struct udevice *dev)
+{
+       struct rp1_clockman *clockman = dev_get_priv(dev);
+
+       spin_lock_init(&clockman->regs_lock);
+
+       clockman->regs = dev_remap_addr(dev);
+       if (!clockman->regs)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct udevice_id rp1_clk_of_match[] = {
+       { .compatible = "raspberrypi,rp1-clocks" },
+       { /* sentinel */ }
+};
+
+static struct clk_ops rp1_clk_ops = {
+       .set_rate = rp1_clock_set_rate,
+       .enable = rp1_clock_on,
+};
+
+U_BOOT_DRIVER(clk_rp1) = {
+       .name = "rp1-clk",
+       .id = UCLASS_CLK,
+       .of_match = rp1_clk_of_match,
+       .probe = rp1_clk_probe,
+       .ops = &rp1_clk_ops,
+       .priv_auto      = sizeof(struct rp1_clockman),
+       .flags = CLK_IGNORE_UNUSED,
+};
diff --git a/include/dt-bindings/clk/rp1.h b/include/dt-bindings/clk/rp1.h
new file mode 100644
index 00000000000..27cbb6e130d
--- /dev/null
+++ b/include/dt-bindings/clk/rp1.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Raspberry Pi Ltd.
+ */
+
+#define RP1_PLL_SYS_CORE               0
+#define RP1_PLL_AUDIO_CORE             1
+#define RP1_PLL_VIDEO_CORE             2
+
+#define RP1_PLL_SYS                    3
+#define RP1_PLL_AUDIO                  4
+#define RP1_PLL_VIDEO                  5
+
+#define RP1_PLL_SYS_PRI_PH             6
+#define RP1_PLL_SYS_SEC_PH             7
+#define RP1_PLL_AUDIO_PRI_PH           8
+
+#define RP1_PLL_SYS_SEC                        9
+#define RP1_PLL_AUDIO_SEC              10
+#define RP1_PLL_VIDEO_SEC              11
+
+#define RP1_CLK_SYS                    12
+#define RP1_CLK_SLOW_SYS               13
+#define RP1_CLK_DMA                    14
+#define RP1_CLK_UART                   15
+#define RP1_CLK_ETH                    16
+#define RP1_CLK_PWM0                   17
+#define RP1_CLK_PWM1                   18
+#define RP1_CLK_AUDIO_IN               19
+#define RP1_CLK_AUDIO_OUT              20
+#define RP1_CLK_I2S                    21
+#define RP1_CLK_MIPI0_CFG              22
+#define RP1_CLK_MIPI1_CFG              23
+#define RP1_CLK_PCIE_AUX               24
+#define RP1_CLK_USBH0_MICROFRAME       25
+#define RP1_CLK_USBH1_MICROFRAME       26
+#define RP1_CLK_USBH0_SUSPEND          27
+#define RP1_CLK_USBH1_SUSPEND          28
+#define RP1_CLK_ETH_TSU                        29
+#define RP1_CLK_ADC                    30
+#define RP1_CLK_SDIO_TIMER             31
+#define RP1_CLK_SDIO_ALT_SRC           32
+#define RP1_CLK_GP0                    33
+#define RP1_CLK_GP1                    34
+#define RP1_CLK_GP2                    35
+#define RP1_CLK_GP3                    36
+#define RP1_CLK_GP4                    37
+#define RP1_CLK_GP5                    38
+#define RP1_CLK_VEC                    39
+#define RP1_CLK_DPI                    40
+#define RP1_CLK_MIPI0_DPI              41
+#define RP1_CLK_MIPI1_DPI              42
+
+/* Extra PLL output channels - RP1B0 only */
+#define RP1_PLL_VIDEO_PRI_PH           43
+#define RP1_PLL_AUDIO_TERN             44
-- 
2.51.0

Reply via email to