[PATCH v4 1/4] clk: sifive: Extract prci core to common base
Palmer Dabbelt
palmer at dabbelt.com
Fri Nov 20 20:29:25 EST 2020
On Wed, 11 Nov 2020 02:06:05 PST (-0800), zong.li at sifive.com wrote:
> Extract common core of prci driver to an independent file, it could
> allow other chips to reuse it. Separate SoCs-dependent code 'fu540'
> from prci core, then we can easily add 'fu740' later.
>
> Almost these changes are code movement. The different is adding the
> private data for each SoC use, so it needs to get match data in probe
> callback function, then use the data for initialization.
There's a bit too much here to track as a diff, so I'm guessing that it's OK.
IIRC we never really intended this to target multiple SOCs, which is why it's a
bit of a mess, so there's really no way around a complicated patch when
splitting this up.
Acked-by: Palmer Dabbelt <palmerdabbelt at google.com>
Thanks!
>
> Signed-off-by: Zong Li <zong.li at sifive.com>
> Reviewed-by: Pragnesh Patel <Pragnesh.patel at sifive.com>
> ---
> drivers/clk/sifive/Makefile | 2 +
> drivers/clk/sifive/fu540-prci.c | 586 +-----------------
> drivers/clk/sifive/fu540-prci.h | 21 +
> .../sifive/{fu540-prci.c => sifive-prci.c} | 381 +++---------
> drivers/clk/sifive/sifive-prci.h | 201 ++++++
> 5 files changed, 323 insertions(+), 868 deletions(-)
> create mode 100644 drivers/clk/sifive/fu540-prci.h
> copy drivers/clk/sifive/{fu540-prci.c => sifive-prci.c} (45%)
> create mode 100644 drivers/clk/sifive/sifive-prci.h
>
> diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
> index 0797f14fef6b..627effe2ece1 100644
> --- a/drivers/clk/sifive/Makefile
> +++ b/drivers/clk/sifive/Makefile
> @@ -1,2 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0-only
> +obj-y += sifive-prci.o
> +
> obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += fu540-prci.o
> diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c
> index a8901f90a61a..840b97bfff85 100644
> --- a/drivers/clk/sifive/fu540-prci.c
> +++ b/drivers/clk/sifive/fu540-prci.c
> @@ -3,6 +3,7 @@
> * Copyright (C) 2018-2019 SiFive, Inc.
> * Wesley Terpstra
> * Paul Walmsley
> + * Zong Li
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 as
> @@ -13,475 +14,48 @@
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> - * The FU540 PRCI implements clock and reset control for the SiFive
> - * FU540-C000 chip. This driver assumes that it has sole control
> - * over all PRCI resources.
> - *
> - * This driver is based on the PRCI driver written by Wesley Terpstra:
> - * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60
> - *
> * References:
> * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset"
> */
>
> #include <dt-bindings/clock/sifive-fu540-prci.h>
> -#include <linux/clkdev.h>
> -#include <linux/clk-provider.h>
> -#include <linux/clk/analogbits-wrpll-cln28hpc.h>
> -#include <linux/delay.h>
> -#include <linux/err.h>
> -#include <linux/io.h>
> #include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/of_clk.h>
> -#include <linux/platform_device.h>
> -#include <linux/slab.h>
> -
> -/*
> - * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects:
> - * hfclk and rtcclk
> - */
> -#define EXPECTED_CLK_PARENT_COUNT 2
> -
> -/*
> - * Register offsets and bitmasks
> - */
> -
> -/* COREPLLCFG0 */
> -#define PRCI_COREPLLCFG0_OFFSET 0x4
> -# define PRCI_COREPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT)
> -# define PRCI_COREPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT)
> -# define PRCI_COREPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_COREPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT)
> -# define PRCI_COREPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_COREPLLCFG0_FSE_SHIFT 25
> -# define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT)
> -# define PRCI_COREPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
> -
> -/* DDRPLLCFG0 */
> -#define PRCI_DDRPLLCFG0_OFFSET 0xc
> -# define PRCI_DDRPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT)
> -# define PRCI_DDRPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT)
> -# define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_DDRPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT)
> -# define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_DDRPLLCFG0_FSE_SHIFT 25
> -# define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT)
> -# define PRCI_DDRPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT)
> +#include "sifive-prci.h"
>
> -/* DDRPLLCFG1 */
> -#define PRCI_DDRPLLCFG1_OFFSET 0x10
> -# define PRCI_DDRPLLCFG1_CKE_SHIFT 24
> -# define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
> +/* PRCI integration data for each WRPLL instance */
>
> -/* GEMGXLPLLCFG0 */
> -#define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c
> -# define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25
> -# define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT)
> -
> -/* GEMGXLPLLCFG1 */
> -#define PRCI_GEMGXLPLLCFG1_OFFSET 0x20
> -# define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24
> -# define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
> -
> -/* CORECLKSEL */
> -#define PRCI_CORECLKSEL_OFFSET 0x24
> -# define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0
> -# define PRCI_CORECLKSEL_CORECLKSEL_MASK (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT)
> -
> -/* DEVICESRESETREG */
> -#define PRCI_DEVICESRESETREG_OFFSET 0x28
> -# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0
> -# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1
> -# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2
> -# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3
> -# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5
> -# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT)
> -
> -/* CLKMUXSTATUSREG */
> -#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c
> -# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1
> -# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT)
> -
> -/*
> - * Private structures
> - */
> -
> -/**
> - * struct __prci_data - per-device-instance data
> - * @va: base virtual address of the PRCI IP block
> - * @hw_clks: encapsulates struct clk_hw records
> - *
> - * PRCI per-device instance data
> - */
> -struct __prci_data {
> - void __iomem *va;
> - struct clk_hw_onecell_data hw_clks;
> +static struct __prci_wrpll_data __prci_corepll_data = {
> + .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
> + .enable_bypass = sifive_prci_coreclksel_use_hfclk,
> + .disable_bypass = sifive_prci_coreclksel_use_corepll,
> };
>
> -/**
> - * struct __prci_wrpll_data - WRPLL configuration and integration data
> - * @c: WRPLL current configuration record
> - * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
> - * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
> - * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
> - *
> - * @enable_bypass and @disable_bypass are used for WRPLL instances
> - * that contain a separate external glitchless clock mux downstream
> - * from the PLL. The WRPLL internal bypass mux is not glitchless.
> - */
> -struct __prci_wrpll_data {
> - struct wrpll_cfg c;
> - void (*enable_bypass)(struct __prci_data *pd);
> - void (*disable_bypass)(struct __prci_data *pd);
> - u8 cfg0_offs;
> +static struct __prci_wrpll_data __prci_ddrpll_data = {
> + .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
> };
>
> -/**
> - * struct __prci_clock - describes a clock device managed by PRCI
> - * @name: user-readable clock name string - should match the manual
> - * @parent_name: parent name for this clock
> - * @ops: struct clk_ops for the Linux clock framework to use for control
> - * @hw: Linux-private clock data
> - * @pwd: WRPLL-specific data, associated with this clock (if not NULL)
> - * @pd: PRCI-specific data associated with this clock (if not NULL)
> - *
> - * PRCI clock data. Used by the PRCI driver to register PRCI-provided
> - * clocks to the Linux clock infrastructure.
> - */
> -struct __prci_clock {
> - const char *name;
> - const char *parent_name;
> - const struct clk_ops *ops;
> - struct clk_hw hw;
> - struct __prci_wrpll_data *pwd;
> - struct __prci_data *pd;
> +static struct __prci_wrpll_data __prci_gemgxlpll_data = {
> + .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
> };
>
> -#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
> -
> -/*
> - * Private functions
> - */
> -
> -/**
> - * __prci_readl() - read from a PRCI register
> - * @pd: PRCI context
> - * @offs: register offset to read from (in bytes, from PRCI base address)
> - *
> - * Read the register located at offset @offs from the base virtual
> - * address of the PRCI register target described by @pd, and return
> - * the value to the caller.
> - *
> - * Context: Any context.
> - *
> - * Return: the contents of the register described by @pd and @offs.
> - */
> -static u32 __prci_readl(struct __prci_data *pd, u32 offs)
> -{
> - return readl_relaxed(pd->va + offs);
> -}
> -
> -static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
> -{
> - writel_relaxed(v, pd->va + offs);
> -}
> -
> -/* WRPLL-related private functions */
> -
> -/**
> - * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
> - * @c: ptr to a struct wrpll_cfg record to write config into
> - * @r: value read from the PRCI PLL configuration register
> - *
> - * Given a value @r read from an FU540 PRCI PLL configuration register,
> - * split it into fields and populate it into the WRPLL configuration record
> - * pointed to by @c.
> - *
> - * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
> - * have the same register layout.
> - *
> - * Context: Any context.
> - */
> -static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
> -{
> - u32 v;
> -
> - v = r & PRCI_COREPLLCFG0_DIVR_MASK;
> - v >>= PRCI_COREPLLCFG0_DIVR_SHIFT;
> - c->divr = v;
> -
> - v = r & PRCI_COREPLLCFG0_DIVF_MASK;
> - v >>= PRCI_COREPLLCFG0_DIVF_SHIFT;
> - c->divf = v;
> -
> - v = r & PRCI_COREPLLCFG0_DIVQ_MASK;
> - v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT;
> - c->divq = v;
> -
> - v = r & PRCI_COREPLLCFG0_RANGE_MASK;
> - v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
> - c->range = v;
> -
> - c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK |
> - WRPLL_FLAGS_EXT_FEEDBACK_MASK);
> -
> - /* external feedback mode not supported */
> - c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
> -}
> -
> -/**
> - * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
> - * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
> - *
> - * Using a set of WRPLL configuration values pointed to by @c,
> - * assemble a PRCI PLL configuration register value, and return it to
> - * the caller.
> - *
> - * Context: Any context. Caller must ensure that the contents of the
> - * record pointed to by @c do not change during the execution
> - * of this function.
> - *
> - * Returns: a value suitable for writing into a PRCI PLL configuration
> - * register
> - */
> -static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
> -{
> - u32 r = 0;
> -
> - r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT;
> - r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT;
> - r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT;
> - r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT;
> -
> - /* external feedback mode not supported */
> - r |= PRCI_COREPLLCFG0_FSE_MASK;
> -
> - return r;
> -}
> -
> -/**
> - * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI
> - * @pd: PRCI context
> - * @pwd: PRCI WRPLL metadata
> - *
> - * Read the current configuration of the PLL identified by @pwd from
> - * the PRCI identified by @pd, and store it into the local configuration
> - * cache in @pwd.
> - *
> - * Context: Any context. Caller must prevent the records pointed to by
> - * @pd and @pwd from changing during execution.
> - */
> -static void __prci_wrpll_read_cfg(struct __prci_data *pd,
> - struct __prci_wrpll_data *pwd)
> -{
> - __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
> -}
> -
> -/**
> - * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI
> - * @pd: PRCI context
> - * @pwd: PRCI WRPLL metadata
> - * @c: WRPLL configuration record to write
> - *
> - * Write the WRPLL configuration described by @c into the WRPLL
> - * configuration register identified by @pwd in the PRCI instance
> - * described by @c. Make a cached copy of the WRPLL's current
> - * configuration so it can be used by other code.
> - *
> - * Context: Any context. Caller must prevent the records pointed to by
> - * @pd and @pwd from changing during execution.
> - */
> -static void __prci_wrpll_write_cfg(struct __prci_data *pd,
> - struct __prci_wrpll_data *pwd,
> - struct wrpll_cfg *c)
> -{
> - __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
> -
> - memcpy(&pwd->c, c, sizeof(*c));
> -}
> -
> -/* Core clock mux control */
> -
> -/**
> - * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
> - * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> - *
> - * Switch the CORECLK mux to the HFCLK input source; return once complete.
> - *
> - * Context: Any context. Caller must prevent concurrent changes to the
> - * PRCI_CORECLKSEL_OFFSET register.
> - */
> -static void __prci_coreclksel_use_hfclk(struct __prci_data *pd)
> -{
> - u32 r;
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> - r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
> - __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> -}
> -
> -/**
> - * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL
> - * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> - *
> - * Switch the CORECLK mux to the PLL output clock; return once complete.
> - *
> - * Context: Any context. Caller must prevent concurrent changes to the
> - * PRCI_CORECLKSEL_OFFSET register.
> - */
> -static void __prci_coreclksel_use_corepll(struct __prci_data *pd)
> -{
> - u32 r;
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> - r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
> - __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> -}
> -
> -/*
> - * Linux clock framework integration
> - *
> - * See the Linux clock framework documentation for more information on
> - * these functions.
> - */
> -
> -static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> -{
> - struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> - struct __prci_wrpll_data *pwd = pc->pwd;
> -
> - return wrpll_calc_output_rate(&pwd->c, parent_rate);
> -}
> -
> -static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw,
> - unsigned long rate,
> - unsigned long *parent_rate)
> -{
> - struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> - struct __prci_wrpll_data *pwd = pc->pwd;
> - struct wrpll_cfg c;
> -
> - memcpy(&c, &pwd->c, sizeof(c));
> -
> - wrpll_configure_for_rate(&c, rate, *parent_rate);
> -
> - return wrpll_calc_output_rate(&c, *parent_rate);
> -}
> -
> -static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw,
> - unsigned long rate,
> - unsigned long parent_rate)
> -{
> - struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> - struct __prci_wrpll_data *pwd = pc->pwd;
> - struct __prci_data *pd = pc->pd;
> - int r;
> -
> - r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate);
> - if (r)
> - return r;
> -
> - if (pwd->enable_bypass)
> - pwd->enable_bypass(pd);
> -
> - __prci_wrpll_write_cfg(pd, pwd, &pwd->c);
> -
> - udelay(wrpll_calc_max_lock_us(&pwd->c));
> -
> - if (pwd->disable_bypass)
> - pwd->disable_bypass(pd);
> -
> - return 0;
> -}
> +/* Linux clock framework integration */
>
> static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
> - .set_rate = sifive_fu540_prci_wrpll_set_rate,
> - .round_rate = sifive_fu540_prci_wrpll_round_rate,
> - .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
> + .set_rate = sifive_prci_wrpll_set_rate,
> + .round_rate = sifive_prci_wrpll_round_rate,
> + .recalc_rate = sifive_prci_wrpll_recalc_rate,
> };
>
> static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
> - .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
> + .recalc_rate = sifive_prci_wrpll_recalc_rate,
> };
>
> -/* TLCLKSEL clock integration */
> -
> -static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> -{
> - struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> - struct __prci_data *pd = pc->pd;
> - u32 v;
> - u8 div;
> -
> - v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET);
> - v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK;
> - div = v ? 1 : 2;
> -
> - return div_u64(parent_rate, div);
> -}
> -
> static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = {
> - .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate,
> -};
> -
> -/*
> - * PRCI integration data for each WRPLL instance
> - */
> -
> -static struct __prci_wrpll_data __prci_corepll_data = {
> - .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
> - .enable_bypass = __prci_coreclksel_use_hfclk,
> - .disable_bypass = __prci_coreclksel_use_corepll,
> -};
> -
> -static struct __prci_wrpll_data __prci_ddrpll_data = {
> - .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
> + .recalc_rate = sifive_prci_tlclksel_recalc_rate,
> };
>
> -static struct __prci_wrpll_data __prci_gemgxlpll_data = {
> - .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
> -};
> -
> -/*
> - * List of clock controls provided by the PRCI
> - */
> -
> -static struct __prci_clock __prci_init_clocks[] = {
> +/* List of clock controls provided by the PRCI */
> +struct __prci_clock __prci_init_clocks_fu540[] = {
> [PRCI_CLK_COREPLL] = {
> .name = "corepll",
> .parent_name = "hfclk",
> @@ -506,125 +80,3 @@ static struct __prci_clock __prci_init_clocks[] = {
> .ops = &sifive_fu540_prci_tlclksel_clk_ops,
> },
> };
> -
> -/**
> - * __prci_register_clocks() - register clock controls in the PRCI with Linux
> - * @dev: Linux struct device *
> - *
> - * Register the list of clock controls described in __prci_init_plls[] with
> - * the Linux clock framework.
> - *
> - * Return: 0 upon success or a negative error code upon failure.
> - */
> -static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
> -{
> - struct clk_init_data init = { };
> - struct __prci_clock *pic;
> - int parent_count, i, r;
> -
> - parent_count = of_clk_get_parent_count(dev->of_node);
> - if (parent_count != EXPECTED_CLK_PARENT_COUNT) {
> - dev_err(dev, "expected only two parent clocks, found %d\n",
> - parent_count);
> - return -EINVAL;
> - }
> -
> - /* Register PLLs */
> - for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) {
> - pic = &__prci_init_clocks[i];
> -
> - init.name = pic->name;
> - init.parent_names = &pic->parent_name;
> - init.num_parents = 1;
> - init.ops = pic->ops;
> - pic->hw.init = &init;
> -
> - pic->pd = pd;
> -
> - if (pic->pwd)
> - __prci_wrpll_read_cfg(pd, pic->pwd);
> -
> - r = devm_clk_hw_register(dev, &pic->hw);
> - if (r) {
> - dev_warn(dev, "Failed to register clock %s: %d\n",
> - init.name, r);
> - return r;
> - }
> -
> - r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev));
> - if (r) {
> - dev_warn(dev, "Failed to register clkdev for %s: %d\n",
> - init.name, r);
> - return r;
> - }
> -
> - pd->hw_clks.hws[i] = &pic->hw;
> - }
> -
> - pd->hw_clks.num = i;
> -
> - r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
> - &pd->hw_clks);
> - if (r) {
> - dev_err(dev, "could not add hw_provider: %d\n", r);
> - return r;
> - }
> -
> - return 0;
> -}
> -
> -/*
> - * Linux device model integration
> - *
> - * See the Linux device model documentation for more information about
> - * these functions.
> - */
> -static int sifive_fu540_prci_probe(struct platform_device *pdev)
> -{
> - struct device *dev = &pdev->dev;
> - struct resource *res;
> - struct __prci_data *pd;
> - int r;
> -
> - pd = devm_kzalloc(dev,
> - struct_size(pd, hw_clks.hws,
> - ARRAY_SIZE(__prci_init_clocks)),
> - GFP_KERNEL);
> - if (!pd)
> - return -ENOMEM;
> -
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - pd->va = devm_ioremap_resource(dev, res);
> - if (IS_ERR(pd->va))
> - return PTR_ERR(pd->va);
> -
> - r = __prci_register_clocks(dev, pd);
> - if (r) {
> - dev_err(dev, "could not register clocks: %d\n", r);
> - return r;
> - }
> -
> - dev_dbg(dev, "SiFive FU540 PRCI probed\n");
> -
> - return 0;
> -}
> -
> -static const struct of_device_id sifive_fu540_prci_of_match[] = {
> - { .compatible = "sifive,fu540-c000-prci", },
> - {}
> -};
> -MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match);
> -
> -static struct platform_driver sifive_fu540_prci_driver = {
> - .driver = {
> - .name = "sifive-fu540-prci",
> - .of_match_table = sifive_fu540_prci_of_match,
> - },
> - .probe = sifive_fu540_prci_probe,
> -};
> -
> -static int __init sifive_fu540_prci_init(void)
> -{
> - return platform_driver_register(&sifive_fu540_prci_driver);
> -}
> -core_initcall(sifive_fu540_prci_init);
> diff --git a/drivers/clk/sifive/fu540-prci.h b/drivers/clk/sifive/fu540-prci.h
> new file mode 100644
> index 000000000000..c8271efa7bdc
> --- /dev/null
> +++ b/drivers/clk/sifive/fu540-prci.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 SiFive, Inc.
> + * Zong Li
> + */
> +
> +#ifndef __SIFIVE_CLK_FU540_PRCI_H
> +#define __SIFIVE_CLK_FU540_PRCI_H
> +
> +#include "sifive-prci.h"
> +
> +#define NUM_CLOCK_FU540 4
> +
> +extern struct __prci_clock __prci_init_clocks_fu540[NUM_CLOCK_FU540];
> +
> +static const struct prci_clk_desc prci_clk_fu540 = {
> + .clks = __prci_init_clocks_fu540,
> + .num_clks = ARRAY_SIZE(__prci_init_clocks_fu540),
> +};
> +
> +#endif /* __SIFIVE_CLK_FU540_PRCI_H */
> diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/sifive-prci.c
> similarity index 45%
> copy from drivers/clk/sifive/fu540-prci.c
> copy to drivers/clk/sifive/sifive-prci.c
> index a8901f90a61a..0ac729eeb71b 100644
> --- a/drivers/clk/sifive/fu540-prci.c
> +++ b/drivers/clk/sifive/sifive-prci.c
> @@ -3,6 +3,7 @@
> * Copyright (C) 2018-2019 SiFive, Inc.
> * Wesley Terpstra
> * Paul Walmsley
> + * Zong Li
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 as
> @@ -13,180 +14,19 @@
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> - * The FU540 PRCI implements clock and reset control for the SiFive
> - * FU540-C000 chip. This driver assumes that it has sole control
> - * over all PRCI resources.
> + * The PRCI implements clock and reset control for the SiFive chip.
> + * This driver assumes that it has sole control over all PRCI resources.
> *
> * This driver is based on the PRCI driver written by Wesley Terpstra:
> * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60
> - *
> - * References:
> - * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset"
> */
>
> -#include <dt-bindings/clock/sifive-fu540-prci.h>
> #include <linux/clkdev.h>
> -#include <linux/clk-provider.h>
> -#include <linux/clk/analogbits-wrpll-cln28hpc.h>
> #include <linux/delay.h>
> -#include <linux/err.h>
> #include <linux/io.h>
> -#include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/of_clk.h>
> -#include <linux/platform_device.h>
> -#include <linux/slab.h>
> -
> -/*
> - * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects:
> - * hfclk and rtcclk
> - */
> -#define EXPECTED_CLK_PARENT_COUNT 2
> -
> -/*
> - * Register offsets and bitmasks
> - */
> -
> -/* COREPLLCFG0 */
> -#define PRCI_COREPLLCFG0_OFFSET 0x4
> -# define PRCI_COREPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT)
> -# define PRCI_COREPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT)
> -# define PRCI_COREPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_COREPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT)
> -# define PRCI_COREPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_COREPLLCFG0_FSE_SHIFT 25
> -# define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT)
> -# define PRCI_COREPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
> -
> -/* DDRPLLCFG0 */
> -#define PRCI_DDRPLLCFG0_OFFSET 0xc
> -# define PRCI_DDRPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT)
> -# define PRCI_DDRPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT)
> -# define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_DDRPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT)
> -# define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_DDRPLLCFG0_FSE_SHIFT 25
> -# define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT)
> -# define PRCI_DDRPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT)
> -
> -/* DDRPLLCFG1 */
> -#define PRCI_DDRPLLCFG1_OFFSET 0x10
> -# define PRCI_DDRPLLCFG1_CKE_SHIFT 24
> -# define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
> -
> -/* GEMGXLPLLCFG0 */
> -#define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c
> -# define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0
> -# define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6
> -# define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15
> -# define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18
> -# define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24
> -# define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25
> -# define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT)
> -# define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31
> -# define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT)
> -
> -/* GEMGXLPLLCFG1 */
> -#define PRCI_GEMGXLPLLCFG1_OFFSET 0x20
> -# define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24
> -# define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
> -
> -/* CORECLKSEL */
> -#define PRCI_CORECLKSEL_OFFSET 0x24
> -# define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0
> -# define PRCI_CORECLKSEL_CORECLKSEL_MASK (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT)
> -
> -/* DEVICESRESETREG */
> -#define PRCI_DEVICESRESETREG_OFFSET 0x28
> -# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0
> -# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1
> -# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2
> -# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3
> -# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT)
> -# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5
> -# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT)
> -
> -/* CLKMUXSTATUSREG */
> -#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c
> -# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1
> -# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT)
> -
> -/*
> - * Private structures
> - */
> -
> -/**
> - * struct __prci_data - per-device-instance data
> - * @va: base virtual address of the PRCI IP block
> - * @hw_clks: encapsulates struct clk_hw records
> - *
> - * PRCI per-device instance data
> - */
> -struct __prci_data {
> - void __iomem *va;
> - struct clk_hw_onecell_data hw_clks;
> -};
> -
> -/**
> - * struct __prci_wrpll_data - WRPLL configuration and integration data
> - * @c: WRPLL current configuration record
> - * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
> - * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
> - * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
> - *
> - * @enable_bypass and @disable_bypass are used for WRPLL instances
> - * that contain a separate external glitchless clock mux downstream
> - * from the PLL. The WRPLL internal bypass mux is not glitchless.
> - */
> -struct __prci_wrpll_data {
> - struct wrpll_cfg c;
> - void (*enable_bypass)(struct __prci_data *pd);
> - void (*disable_bypass)(struct __prci_data *pd);
> - u8 cfg0_offs;
> -};
> -
> -/**
> - * struct __prci_clock - describes a clock device managed by PRCI
> - * @name: user-readable clock name string - should match the manual
> - * @parent_name: parent name for this clock
> - * @ops: struct clk_ops for the Linux clock framework to use for control
> - * @hw: Linux-private clock data
> - * @pwd: WRPLL-specific data, associated with this clock (if not NULL)
> - * @pd: PRCI-specific data associated with this clock (if not NULL)
> - *
> - * PRCI clock data. Used by the PRCI driver to register PRCI-provided
> - * clocks to the Linux clock infrastructure.
> - */
> -struct __prci_clock {
> - const char *name;
> - const char *parent_name;
> - const struct clk_ops *ops;
> - struct clk_hw hw;
> - struct __prci_wrpll_data *pwd;
> - struct __prci_data *pd;
> -};
> -
> -#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
> +#include <linux/of_device.h>
> +#include "sifive-prci.h"
> +#include "fu540-prci.h"
>
> /*
> * Private functions
> @@ -222,7 +62,7 @@ static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
> * @c: ptr to a struct wrpll_cfg record to write config into
> * @r: value read from the PRCI PLL configuration register
> *
> - * Given a value @r read from an FU540 PRCI PLL configuration register,
> + * Given a value @r read from an FU740 PRCI PLL configuration register,
> * split it into fields and populate it into the WRPLL configuration record
> * pointed to by @c.
> *
> @@ -251,8 +91,8 @@ static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
> v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
> c->range = v;
>
> - c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK |
> - WRPLL_FLAGS_EXT_FEEDBACK_MASK);
> + c->flags &=
> + (WRPLL_FLAGS_INT_FEEDBACK_MASK | WRPLL_FLAGS_EXT_FEEDBACK_MASK);
>
> /* external feedback mode not supported */
> c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
> @@ -329,48 +169,6 @@ static void __prci_wrpll_write_cfg(struct __prci_data *pd,
> memcpy(&pwd->c, c, sizeof(*c));
> }
>
> -/* Core clock mux control */
> -
> -/**
> - * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
> - * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> - *
> - * Switch the CORECLK mux to the HFCLK input source; return once complete.
> - *
> - * Context: Any context. Caller must prevent concurrent changes to the
> - * PRCI_CORECLKSEL_OFFSET register.
> - */
> -static void __prci_coreclksel_use_hfclk(struct __prci_data *pd)
> -{
> - u32 r;
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> - r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
> - __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> -}
> -
> -/**
> - * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL
> - * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> - *
> - * Switch the CORECLK mux to the PLL output clock; return once complete.
> - *
> - * Context: Any context. Caller must prevent concurrent changes to the
> - * PRCI_CORECLKSEL_OFFSET register.
> - */
> -static void __prci_coreclksel_use_corepll(struct __prci_data *pd)
> -{
> - u32 r;
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> - r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
> - __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
> -
> - r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> -}
> -
> /*
> * Linux clock framework integration
> *
> @@ -378,8 +176,8 @@ static void __prci_coreclksel_use_corepll(struct __prci_data *pd)
> * these functions.
> */
>
> -static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> +unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> {
> struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> struct __prci_wrpll_data *pwd = pc->pwd;
> @@ -387,9 +185,9 @@ static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw,
> return wrpll_calc_output_rate(&pwd->c, parent_rate);
> }
>
> -static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw,
> - unsigned long rate,
> - unsigned long *parent_rate)
> +long sifive_prci_wrpll_round_rate(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *parent_rate)
> {
> struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> struct __prci_wrpll_data *pwd = pc->pwd;
> @@ -402,9 +200,8 @@ static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw,
> return wrpll_calc_output_rate(&c, *parent_rate);
> }
>
> -static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw,
> - unsigned long rate,
> - unsigned long parent_rate)
> +int sifive_prci_wrpll_set_rate(struct clk_hw *hw,
> + unsigned long rate, unsigned long parent_rate)
> {
> struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> struct __prci_wrpll_data *pwd = pc->pwd;
> @@ -428,20 +225,10 @@ static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw,
> return 0;
> }
>
> -static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
> - .set_rate = sifive_fu540_prci_wrpll_set_rate,
> - .round_rate = sifive_fu540_prci_wrpll_round_rate,
> - .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
> -};
> -
> -static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
> - .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
> -};
> -
> /* TLCLKSEL clock integration */
>
> -static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> +unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> {
> struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
> struct __prci_data *pd = pc->pd;
> @@ -455,68 +242,62 @@ static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw,
> return div_u64(parent_rate, div);
> }
>
> -static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = {
> - .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate,
> -};
> -
> /*
> - * PRCI integration data for each WRPLL instance
> + * Core clock mux control
> */
>
> -static struct __prci_wrpll_data __prci_corepll_data = {
> - .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
> - .enable_bypass = __prci_coreclksel_use_hfclk,
> - .disable_bypass = __prci_coreclksel_use_corepll,
> -};
> +/**
> + * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
> + * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> + *
> + * Switch the CORECLK mux to the HFCLK input source; return once complete.
> + *
> + * Context: Any context. Caller must prevent concurrent changes to the
> + * PRCI_CORECLKSEL_OFFSET register.
> + */
> +void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd)
> +{
> + u32 r;
>
> -static struct __prci_wrpll_data __prci_ddrpll_data = {
> - .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
> -};
> + r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> + r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
> + __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
>
> -static struct __prci_wrpll_data __prci_gemgxlpll_data = {
> - .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
> -};
> + r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> +}
>
> -/*
> - * List of clock controls provided by the PRCI
> +/**
> + * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output
> + * COREPLL
> + * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
> + *
> + * Switch the CORECLK mux to the COREPLL output clock; return once complete.
> + *
> + * Context: Any context. Caller must prevent concurrent changes to the
> + * PRCI_CORECLKSEL_OFFSET register.
> */
> +void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd)
> +{
> + u32 r;
>
> -static struct __prci_clock __prci_init_clocks[] = {
> - [PRCI_CLK_COREPLL] = {
> - .name = "corepll",
> - .parent_name = "hfclk",
> - .ops = &sifive_fu540_prci_wrpll_clk_ops,
> - .pwd = &__prci_corepll_data,
> - },
> - [PRCI_CLK_DDRPLL] = {
> - .name = "ddrpll",
> - .parent_name = "hfclk",
> - .ops = &sifive_fu540_prci_wrpll_ro_clk_ops,
> - .pwd = &__prci_ddrpll_data,
> - },
> - [PRCI_CLK_GEMGXLPLL] = {
> - .name = "gemgxlpll",
> - .parent_name = "hfclk",
> - .ops = &sifive_fu540_prci_wrpll_clk_ops,
> - .pwd = &__prci_gemgxlpll_data,
> - },
> - [PRCI_CLK_TLCLK] = {
> - .name = "tlclk",
> - .parent_name = "corepll",
> - .ops = &sifive_fu540_prci_tlclksel_clk_ops,
> - },
> -};
> + r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
> + r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
> + __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
> +
> + r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
> +}
>
> /**
> - * __prci_register_clocks() - register clock controls in the PRCI with Linux
> + * __prci_register_clocks() - register clock controls in the PRCI
> * @dev: Linux struct device *
> *
> - * Register the list of clock controls described in __prci_init_plls[] with
> + * Register the list of clock controls described in __prci_init_clocks[] with
> * the Linux clock framework.
> *
> * Return: 0 upon success or a negative error code upon failure.
> */
> -static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
> +static int __prci_register_clocks(struct device *dev, struct __prci_data *pd,
> + const struct prci_clk_desc *desc)
> {
> struct clk_init_data init = { };
> struct __prci_clock *pic;
> @@ -530,8 +311,8 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
> }
>
> /* Register PLLs */
> - for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) {
> - pic = &__prci_init_clocks[i];
> + for (i = 0; i < desc->num_clks; ++i) {
> + pic = &(desc->clks[i]);
>
> init.name = pic->name;
> init.parent_names = &pic->parent_name;
> @@ -573,23 +354,22 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
> return 0;
> }
>
> -/*
> - * Linux device model integration
> +/**
> + * sifive_prci_init() - initialize prci data and check parent count
> *
> - * See the Linux device model documentation for more information about
> - * these functions.
> + * Return: 0 upon success or a negative error code upon failure.
> */
> -static int sifive_fu540_prci_probe(struct platform_device *pdev)
> +static int sifive_prci_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> struct resource *res;
> struct __prci_data *pd;
> + const struct prci_clk_desc *desc;
> int r;
>
> - pd = devm_kzalloc(dev,
> - struct_size(pd, hw_clks.hws,
> - ARRAY_SIZE(__prci_init_clocks)),
> - GFP_KERNEL);
> + desc = of_device_get_match_data(&pdev->dev);
> +
> + pd = devm_kzalloc(dev, struct_size(pd, hw_clks.hws, desc->num_clks), GFP_KERNEL);
> if (!pd)
> return -ENOMEM;
>
> @@ -598,33 +378,32 @@ static int sifive_fu540_prci_probe(struct platform_device *pdev)
> if (IS_ERR(pd->va))
> return PTR_ERR(pd->va);
>
> - r = __prci_register_clocks(dev, pd);
> + r = __prci_register_clocks(dev, pd, desc);
> if (r) {
> dev_err(dev, "could not register clocks: %d\n", r);
> return r;
> }
>
> - dev_dbg(dev, "SiFive FU540 PRCI probed\n");
> + dev_dbg(dev, "SiFive PRCI probed\n");
>
> return 0;
> }
>
> -static const struct of_device_id sifive_fu540_prci_of_match[] = {
> - { .compatible = "sifive,fu540-c000-prci", },
> +static const struct of_device_id sifive_prci_of_match[] = {
> + {.compatible = "sifive,fu540-c000-prci", .data = &prci_clk_fu540},
> {}
> };
> -MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match);
>
> -static struct platform_driver sifive_fu540_prci_driver = {
> - .driver = {
> - .name = "sifive-fu540-prci",
> - .of_match_table = sifive_fu540_prci_of_match,
> +static struct platform_driver sifive_prci_driver = {
> + .driver = {
> + .name = "sifive-clk-prci",
> + .of_match_table = sifive_prci_of_match,
> },
> - .probe = sifive_fu540_prci_probe,
> + .probe = sifive_prci_probe,
> };
>
> -static int __init sifive_fu540_prci_init(void)
> +static int __init sifive_prci_init(void)
> {
> - return platform_driver_register(&sifive_fu540_prci_driver);
> + return platform_driver_register(&sifive_prci_driver);
> }
> -core_initcall(sifive_fu540_prci_init);
> +core_initcall(sifive_prci_init);
> diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
> new file mode 100644
> index 000000000000..025f717bc053
> --- /dev/null
> +++ b/drivers/clk/sifive/sifive-prci.h
> @@ -0,0 +1,201 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2018-2019 SiFive, Inc.
> + * Wesley Terpstra
> + * Paul Walmsley
> + * Zong Li
> + */
> +
> +#ifndef __SIFIVE_CLK_SIFIVE_PRCI_H
> +#define __SIFIVE_CLK_SIFIVE_PRCI_H
> +
> +#include <linux/clk/analogbits-wrpll-cln28hpc.h>
> +#include <linux/clk-provider.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects:
> + * hfclk and rtcclk
> + */
> +#define EXPECTED_CLK_PARENT_COUNT 2
> +
> +/*
> + * Register offsets and bitmasks
> + */
> +
> +/* COREPLLCFG0 */
> +#define PRCI_COREPLLCFG0_OFFSET 0x4
> +#define PRCI_COREPLLCFG0_DIVR_SHIFT 0
> +#define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT)
> +#define PRCI_COREPLLCFG0_DIVF_SHIFT 6
> +#define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT)
> +#define PRCI_COREPLLCFG0_DIVQ_SHIFT 15
> +#define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT)
> +#define PRCI_COREPLLCFG0_RANGE_SHIFT 18
> +#define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT)
> +#define PRCI_COREPLLCFG0_BYPASS_SHIFT 24
> +#define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT)
> +#define PRCI_COREPLLCFG0_FSE_SHIFT 25
> +#define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT)
> +#define PRCI_COREPLLCFG0_LOCK_SHIFT 31
> +#define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
> +
> +/* DDRPLLCFG0 */
> +#define PRCI_DDRPLLCFG0_OFFSET 0xc
> +#define PRCI_DDRPLLCFG0_DIVR_SHIFT 0
> +#define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT)
> +#define PRCI_DDRPLLCFG0_DIVF_SHIFT 6
> +#define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT)
> +#define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15
> +#define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT)
> +#define PRCI_DDRPLLCFG0_RANGE_SHIFT 18
> +#define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT)
> +#define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24
> +#define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT)
> +#define PRCI_DDRPLLCFG0_FSE_SHIFT 25
> +#define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT)
> +#define PRCI_DDRPLLCFG0_LOCK_SHIFT 31
> +#define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT)
> +
> +/* DDRPLLCFG1 */
> +#define PRCI_DDRPLLCFG1_OFFSET 0x10
> +#define PRCI_DDRPLLCFG1_CKE_SHIFT 24
> +#define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
> +
> +/* GEMGXLPLLCFG0 */
> +#define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c
> +#define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0
> +#define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6
> +#define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15
> +#define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18
> +#define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24
> +#define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25
> +#define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT)
> +#define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31
> +#define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT)
> +
> +/* GEMGXLPLLCFG1 */
> +#define PRCI_GEMGXLPLLCFG1_OFFSET 0x20
> +#define RCI_GEMGXLPLLCFG1_CKE_SHIFT 24
> +#define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
> +
> +/* CORECLKSEL */
> +#define PRCI_CORECLKSEL_OFFSET 0x24
> +#define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0
> +#define PRCI_CORECLKSEL_CORECLKSEL_MASK \
> + (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT)
> +
> +/* DEVICESRESETREG */
> +#define PRCI_DEVICESRESETREG_OFFSET 0x28
> +#define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0
> +#define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT)
> +#define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1
> +#define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT)
> +#define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2
> +#define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT)
> +#define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3
> +#define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT)
> +#define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5
> +#define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT)
> +#define PRCI_DEVICESRESETREG_CHIPLINK_RST_N_SHIFT 6
> +#define PRCI_DEVICESRESETREG_CHIPLINK_RST_N_MASK \
> + (0x1 << PRCI_DEVICESRESETREG_CHIPLINK_RST_N_SHIFT)
> +
> +/* CLKMUXSTATUSREG */
> +#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c
> +#define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1
> +#define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK \
> + (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT)
> +
> +/*
> + * Private structures
> + */
> +
> +/**
> + * struct __prci_data - per-device-instance data
> + * @va: base virtual address of the PRCI IP block
> + * @hw_clks: encapsulates struct clk_hw records
> + *
> + * PRCI per-device instance data
> + */
> +struct __prci_data {
> + void __iomem *va;
> + struct clk_hw_onecell_data hw_clks;
> +};
> +
> +/**
> + * struct __prci_wrpll_data - WRPLL configuration and integration data
> + * @c: WRPLL current configuration record
> + * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
> + * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
> + * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
> + *
> + * @enable_bypass and @disable_bypass are used for WRPLL instances
> + * that contain a separate external glitchless clock mux downstream
> + * from the PLL. The WRPLL internal bypass mux is not glitchless.
> + */
> +struct __prci_wrpll_data {
> + struct wrpll_cfg c;
> + void (*enable_bypass)(struct __prci_data *pd);
> + void (*disable_bypass)(struct __prci_data *pd);
> + u8 cfg0_offs;
> +};
> +
> +/**
> + * struct __prci_clock - describes a clock device managed by PRCI
> + * @name: user-readable clock name string - should match the manual
> + * @parent_name: parent name for this clock
> + * @ops: struct clk_ops for the Linux clock framework to use for control
> + * @hw: Linux-private clock data
> + * @pwd: WRPLL-specific data, associated with this clock (if not NULL)
> + * @pd: PRCI-specific data associated with this clock (if not NULL)
> + *
> + * PRCI clock data. Used by the PRCI driver to register PRCI-provided
> + * clocks to the Linux clock infrastructure.
> + */
> +struct __prci_clock {
> + const char *name;
> + const char *parent_name;
> + const struct clk_ops *ops;
> + struct clk_hw hw;
> + struct __prci_wrpll_data *pwd;
> + struct __prci_data *pd;
> +};
> +
> +#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
> +
> +/*
> + * struct prci_clk_desc - describes the information of clocks of each SoCs
> + * @clks: point to a array of __prci_clock
> + * @num_clks: the number of element of clks
> + */
> +struct prci_clk_desc {
> + struct __prci_clock *clks;
> + size_t num_clks;
> +};
> +
> +/* Core clock mux control */
> +void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd);
> +void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd);
> +
> +/* Linux clock framework integration */
> +long sifive_prci_wrpll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *parent_rate);
> +int sifive_prci_wrpll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate);
> +unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate);
> +unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate);
> +
> +#endif /* __SIFIVE_CLK_SIFIVE_PRCI_H */
More information about the linux-riscv
mailing list