summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig11
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/stm32/Kconfig29
-rw-r--r--drivers/clk/stm32/Makefile1
-rw-r--r--drivers/clk/stm32/clk-stm32-core.c5
-rw-r--r--drivers/clk/stm32/clk-stm32-core.h5
-rw-r--r--drivers/clk/stm32/clk-stm32mp1.c (renamed from drivers/clk/clk-stm32mp1.c)127
-rw-r--r--drivers/clk/stm32/clk-stm32mp13.c9
-rw-r--r--drivers/clk/stm32/reset-stm32.c14
-rw-r--r--drivers/clk/stm32/reset-stm32.h8
-rw-r--r--drivers/clk/xilinx/clk-xlnx-clock-wizard.c628
-rw-r--r--drivers/clk/zynqmp/clk-mux-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/divider.c66
13 files changed, 614 insertions, 292 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c30d0d396f7a..50af5fc7f570 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -414,16 +414,6 @@ config COMMON_CLK_VC7
Renesas Versaclock7 is a family of configurable clock generator
and jitter attenuator ICs with fractional and integer dividers.
-config COMMON_CLK_STM32MP135
- def_bool COMMON_CLK && MACH_STM32MP13
- help
- Support for stm32mp135 SoC family clocks
-
-config COMMON_CLK_STM32MP157
- def_bool COMMON_CLK && MACH_STM32MP157
- help
- Support for stm32mp157 SoC family clocks
-
config COMMON_CLK_STM32F
def_bool COMMON_CLK && (MACH_STM32F429 || MACH_STM32F469 || MACH_STM32F746)
help
@@ -504,6 +494,7 @@ source "drivers/clk/starfive/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/stm32/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/visconti/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index ed71f2e0ee36..14fa8d4ecc1f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -70,7 +70,6 @@ obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
obj-$(CONFIG_COMMON_CLK_SP7021) += clk-sp7021.o
obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o
-obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o
obj-$(CONFIG_COMMON_CLK_TPS68470) += clk-tps68470.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
obj-$(CONFIG_CLK_TWL) += clk-twl.o
diff --git a/drivers/clk/stm32/Kconfig b/drivers/clk/stm32/Kconfig
new file mode 100644
index 000000000000..3c8493a94a11
--- /dev/null
+++ b/drivers/clk/stm32/Kconfig
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# common clock support for STMicroelectronics SoC family.
+
+menuconfig COMMON_CLK_STM32MP
+ bool "Clock support for common STM32MP clocks"
+ depends on ARCH_STM32 || COMPILE_TEST
+ default y
+ select RESET_CONTROLLER
+ help
+ Support for STM32MP SoC family clocks.
+
+if COMMON_CLK_STM32MP
+
+config COMMON_CLK_STM32MP135
+ bool "Clock driver for stm32mp13x clocks"
+ depends on ARM || COMPILE_TEST
+ default y
+ help
+ Support for stm32mp13x SoC family clocks.
+
+config COMMON_CLK_STM32MP157
+ bool "Clock driver for stm32mp15x clocks"
+ depends on ARM || COMPILE_TEST
+ default y
+ help
+ Support for stm32mp15x SoC family clocks.
+
+endif
+
diff --git a/drivers/clk/stm32/Makefile b/drivers/clk/stm32/Makefile
index 95bd2230bba0..5ced7fe3ddec 100644
--- a/drivers/clk/stm32/Makefile
+++ b/drivers/clk/stm32/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_COMMON_CLK_STM32MP135) += clk-stm32mp13.o clk-stm32-core.o reset-stm32.o
+obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o reset-stm32.o
diff --git a/drivers/clk/stm32/clk-stm32-core.c b/drivers/clk/stm32/clk-stm32-core.c
index 067b918a8894..58705fcad334 100644
--- a/drivers/clk/stm32/clk-stm32-core.c
+++ b/drivers/clk/stm32/clk-stm32-core.c
@@ -70,6 +70,7 @@ static int stm32_rcc_clock_init(struct device *dev,
int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data,
void __iomem *base)
{
+ const struct stm32_rcc_match_data *rcc_match_data;
const struct of_device_id *match;
int err;
@@ -79,8 +80,10 @@ int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data,
return -ENODEV;
}
+ rcc_match_data = match->data;
+
/* RCC Reset Configuration */
- err = stm32_rcc_reset_init(dev, match, base);
+ err = stm32_rcc_reset_init(dev, rcc_match_data->reset_data, base);
if (err) {
pr_err("stm32 reset failed to initialize\n");
return err;
diff --git a/drivers/clk/stm32/clk-stm32-core.h b/drivers/clk/stm32/clk-stm32-core.h
index 76cffda02308..bb5aa19a792d 100644
--- a/drivers/clk/stm32/clk-stm32-core.h
+++ b/drivers/clk/stm32/clk-stm32-core.h
@@ -70,15 +70,12 @@ struct stm32_rcc_match_data {
const struct clock_config *tab_clocks;
unsigned int maxbinding;
struct clk_stm32_clock_data *clock_data;
- u32 clear_offset;
+ struct clk_stm32_reset_data *reset_data;
int (*check_security)(void __iomem *base,
const struct clock_config *cfg);
int (*multi_mux)(void __iomem *base, const struct clock_config *cfg);
};
-int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
- void __iomem *base);
-
int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data,
void __iomem *base);
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c
index 939779f66867..7e2337297402 100644
--- a/drivers/clk/clk-stm32mp1.c
+++ b/drivers/clk/stm32/clk-stm32mp1.c
@@ -20,6 +20,10 @@
#include <dt-bindings/clock/stm32mp1-clks.h>
+#include "reset-stm32.h"
+
+#define STM32MP1_RESET_ID_MASK GENMASK(15, 0)
+
static DEFINE_SPINLOCK(rlock);
#define RCC_OCENSETR 0x0C
@@ -2137,22 +2141,27 @@ struct stm32_rcc_match_data {
const struct clock_config *cfg;
unsigned int num;
unsigned int maxbinding;
- u32 clear_offset;
+ struct clk_stm32_reset_data *reset_data;
bool (*check_security)(const struct clock_config *cfg);
};
+static struct clk_stm32_reset_data stm32mp1_reset_data = {
+ .nr_lines = STM32MP1_RESET_ID_MASK,
+ .clear_offset = RCC_CLR,
+};
+
static struct stm32_rcc_match_data stm32mp1_data = {
.cfg = stm32mp1_clock_cfg,
.num = ARRAY_SIZE(stm32mp1_clock_cfg),
.maxbinding = STM32MP1_LAST_CLK,
- .clear_offset = RCC_CLR,
+ .reset_data = &stm32mp1_reset_data,
};
static struct stm32_rcc_match_data stm32mp1_data_secure = {
.cfg = stm32mp1_clock_cfg,
.num = ARRAY_SIZE(stm32mp1_clock_cfg),
.maxbinding = STM32MP1_LAST_CLK,
- .clear_offset = RCC_CLR,
+ .reset_data = &stm32mp1_reset_data,
.check_security = &stm32_check_security
};
@@ -2193,113 +2202,6 @@ static int stm32_register_hw_clk(struct device *dev,
return 0;
}
-#define STM32_RESET_ID_MASK GENMASK(15, 0)
-
-struct stm32_reset_data {
- /* reset lock */
- spinlock_t lock;
- struct reset_controller_dev rcdev;
- void __iomem *membase;
- u32 clear_offset;
-};
-
-static inline struct stm32_reset_data *
-to_stm32_reset_data(struct reset_controller_dev *rcdev)
-{
- return container_of(rcdev, struct stm32_reset_data, rcdev);
-}
-
-static int stm32_reset_update(struct reset_controller_dev *rcdev,
- unsigned long id, bool assert)
-{
- struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
- int reg_width = sizeof(u32);
- int bank = id / (reg_width * BITS_PER_BYTE);
- int offset = id % (reg_width * BITS_PER_BYTE);
-
- if (data->clear_offset) {
- void __iomem *addr;
-
- addr = data->membase + (bank * reg_width);
- if (!assert)
- addr += data->clear_offset;
-
- writel(BIT(offset), addr);
-
- } else {
- unsigned long flags;
- u32 reg;
-
- spin_lock_irqsave(&data->lock, flags);
-
- reg = readl(data->membase + (bank * reg_width));
-
- if (assert)
- reg |= BIT(offset);
- else
- reg &= ~BIT(offset);
-
- writel(reg, data->membase + (bank * reg_width));
-
- spin_unlock_irqrestore(&data->lock, flags);
- }
-
- return 0;
-}
-
-static int stm32_reset_assert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return stm32_reset_update(rcdev, id, true);
-}
-
-static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return stm32_reset_update(rcdev, id, false);
-}
-
-static int stm32_reset_status(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
- int reg_width = sizeof(u32);
- int bank = id / (reg_width * BITS_PER_BYTE);
- int offset = id % (reg_width * BITS_PER_BYTE);
- u32 reg;
-
- reg = readl(data->membase + (bank * reg_width));
-
- return !!(reg & BIT(offset));
-}
-
-static const struct reset_control_ops stm32_reset_ops = {
- .assert = stm32_reset_assert,
- .deassert = stm32_reset_deassert,
- .status = stm32_reset_status,
-};
-
-static int stm32_rcc_reset_init(struct device *dev, void __iomem *base,
- const struct of_device_id *match)
-{
- const struct stm32_rcc_match_data *data = match->data;
- struct stm32_reset_data *reset_data = NULL;
-
- reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
- if (!reset_data)
- return -ENOMEM;
-
- spin_lock_init(&reset_data->lock);
- reset_data->membase = base;
- reset_data->rcdev.owner = THIS_MODULE;
- reset_data->rcdev.ops = &stm32_reset_ops;
- reset_data->rcdev.of_node = dev_of_node(dev);
- reset_data->rcdev.nr_resets = STM32_RESET_ID_MASK;
- reset_data->clear_offset = data->clear_offset;
-
- return reset_controller_register(&reset_data->rcdev);
-}
-
static int stm32_rcc_clock_init(struct device *dev, void __iomem *base,
const struct of_device_id *match)
{
@@ -2342,6 +2244,7 @@ static int stm32_rcc_clock_init(struct device *dev, void __iomem *base,
static int stm32_rcc_init(struct device *dev, void __iomem *base,
const struct of_device_id *match_data)
{
+ const struct stm32_rcc_match_data *rcc_match_data;
const struct of_device_id *match;
int err;
@@ -2351,8 +2254,10 @@ static int stm32_rcc_init(struct device *dev, void __iomem *base,
return -ENODEV;
}
+ rcc_match_data = match->data;
+
/* RCC Reset Configuration */
- err = stm32_rcc_reset_init(dev, base, match);
+ err = stm32_rcc_reset_init(dev, rcc_match_data->reset_data, base);
if (err) {
pr_err("stm32mp1 reset failed to initialize\n");
return err;
diff --git a/drivers/clk/stm32/clk-stm32mp13.c b/drivers/clk/stm32/clk-stm32mp13.c
index c4a737482fe5..d4ecb3c34a1b 100644
--- a/drivers/clk/stm32/clk-stm32mp13.c
+++ b/drivers/clk/stm32/clk-stm32mp13.c
@@ -10,8 +10,10 @@
#include <linux/platform_device.h>
#include <dt-bindings/clock/stm32mp13-clks.h>
#include "clk-stm32-core.h"
+#include "reset-stm32.h"
#include "stm32mp13_rcc.h"
+#define STM32MP1_RESET_ID_MASK GENMASK(15, 0)
#define RCC_CLR_OFFSET 0x4
/* STM32 Gates definition */
@@ -1511,13 +1513,18 @@ static struct clk_stm32_clock_data stm32mp13_clock_data = {
.is_multi_mux = stm32mp13_is_multi_mux,
};
+static struct clk_stm32_reset_data stm32mp13_reset_data = {
+ .nr_lines = STM32MP1_RESET_ID_MASK,
+ .clear_offset = RCC_CLR_OFFSET,
+};
+
static const struct stm32_rcc_match_data stm32mp13_data = {
.tab_clocks = stm32mp13_clock_cfg,
.num_clocks = ARRAY_SIZE(stm32mp13_clock_cfg),
.clock_data = &stm32mp13_clock_data,
.check_security = &stm32mp13_clock_is_provided_by_secure,
.maxbinding = STM32MP1_LAST_CLK,
- .clear_offset = RCC_CLR_OFFSET,
+ .reset_data = &stm32mp13_reset_data,
};
static const struct of_device_id stm32mp13_match_data[] = {
diff --git a/drivers/clk/stm32/reset-stm32.c b/drivers/clk/stm32/reset-stm32.c
index e89381528af9..14c2ee1eebee 100644
--- a/drivers/clk/stm32/reset-stm32.c
+++ b/drivers/clk/stm32/reset-stm32.c
@@ -11,9 +11,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include "clk-stm32-core.h"
-
-#define STM32_RESET_ID_MASK GENMASK(15, 0)
+#include "reset-stm32.h"
struct stm32_reset_data {
/* reset lock */
@@ -99,24 +97,22 @@ static const struct reset_control_ops stm32_reset_ops = {
.status = stm32_reset_status,
};
-int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
+int stm32_rcc_reset_init(struct device *dev, struct clk_stm32_reset_data *data,
void __iomem *base)
{
- const struct stm32_rcc_match_data *data = match->data;
- struct stm32_reset_data *reset_data = NULL;
-
- data = match->data;
+ struct stm32_reset_data *reset_data;
reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
if (!reset_data)
return -ENOMEM;
spin_lock_init(&reset_data->lock);
+
reset_data->membase = base;
reset_data->rcdev.owner = THIS_MODULE;
reset_data->rcdev.ops = &stm32_reset_ops;
reset_data->rcdev.of_node = dev_of_node(dev);
- reset_data->rcdev.nr_resets = STM32_RESET_ID_MASK;
+ reset_data->rcdev.nr_resets = data->nr_lines;
reset_data->clear_offset = data->clear_offset;
return reset_controller_register(&reset_data->rcdev);
diff --git a/drivers/clk/stm32/reset-stm32.h b/drivers/clk/stm32/reset-stm32.h
index 6eb6ea4b55ab..8cf1cc9be480 100644
--- a/drivers/clk/stm32/reset-stm32.h
+++ b/drivers/clk/stm32/reset-stm32.h
@@ -4,5 +4,11 @@
* Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
*/
-int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
+struct clk_stm32_reset_data {
+ const struct reset_control_ops *ops;
+ unsigned int nr_lines;
+ u32 clear_offset;
+};
+
+int stm32_rcc_reset_init(struct device *dev, struct clk_stm32_reset_data *data,
void __iomem *base);
diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c
index d56822ce6126..6a6e5d9292e8 100644
--- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c
+++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c
@@ -23,15 +23,41 @@
#define WZRD_NUM_OUTPUTS 7
#define WZRD_ACLK_MAX_FREQ 250000000UL
-#define WZRD_CLK_CFG_REG(n) (0x200 + 4 * (n))
+#define WZRD_CLK_CFG_REG(v, n) (0x200 + 0x130 * (v) + 4 * (n))
#define WZRD_CLKOUT0_FRAC_EN BIT(18)
-#define WZRD_CLKFBOUT_FRAC_EN BIT(26)
+#define WZRD_CLKFBOUT_1 0
+#define WZRD_CLKFBOUT_2 1
+#define WZRD_CLKOUT0_1 2
+#define WZRD_CLKOUT0_2 3
+#define WZRD_DESKEW_2 20
+#define WZRD_DIVCLK 21
+#define WZRD_CLKFBOUT_4 51
+#define WZRD_CLKFBOUT_3 48
+#define WZRD_DUTY_CYCLE 2
+#define WZRD_O_DIV 4
+
+#define WZRD_CLKFBOUT_FRAC_EN BIT(1)
+#define WZRD_CLKFBOUT_PREDIV2 (BIT(11) | BIT(12) | BIT(9))
+#define WZRD_MULT_PREDIV2 (BIT(10) | BIT(9) | BIT(12))
+#define WZRD_CLKFBOUT_EDGE BIT(8)
+#define WZRD_P5EN BIT(13)
+#define WZRD_P5EN_SHIFT 13
+#define WZRD_P5FEDGE BIT(15)
+#define WZRD_DIVCLK_EDGE BIT(10)
+#define WZRD_P5FEDGE_SHIFT 15
+#define WZRD_CLKOUT0_PREDIV2 BIT(11)
+#define WZRD_EDGE_SHIFT 8
#define WZRD_CLKFBOUT_MULT_SHIFT 8
#define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
+#define WZRD_CLKFBOUT_L_SHIFT 0
+#define WZRD_CLKFBOUT_H_SHIFT 8
+#define WZRD_CLKFBOUT_L_MASK GENMASK(7, 0)
+#define WZRD_CLKFBOUT_H_MASK GENMASK(15, 8)
#define WZRD_CLKFBOUT_FRAC_SHIFT 16
#define WZRD_CLKFBOUT_FRAC_MASK (0x3ff << WZRD_CLKFBOUT_FRAC_SHIFT)
+#define WZRD_VERSAL_FRAC_MASK GENMASK(5, 0)
#define WZRD_DIVCLK_DIVIDE_SHIFT 0
#define WZRD_DIVCLK_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
#define WZRD_CLKOUT_DIVIDE_SHIFT 0
@@ -45,6 +71,7 @@
#define WZRD_DR_STATUS_REG_OFFSET 0x04
#define WZRD_DR_LOCK_BIT_MASK 0x00000001
#define WZRD_DR_INIT_REG_OFFSET 0x25C
+#define WZRD_DR_INIT_VERSAL_OFFSET 0x14
#define WZRD_DR_DIV_TO_PHASE_OFFSET 4
#define WZRD_DR_BEGIN_DYNA_RECONF 0x03
#define WZRD_DR_BEGIN_DYNA_RECONF_5_2 0x07
@@ -52,6 +79,8 @@
#define WZRD_USEC_POLL 10
#define WZRD_TIMEOUT_POLL 1000
+#define WZRD_FRAC_GRADIENT 64
+#define PREDIV2_MULT 2
/* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
#define DIV_O 0x01
@@ -65,6 +94,14 @@
#define WZRD_VCO_MAX 1600000000
#define WZRD_O_MIN 1
#define WZRD_O_MAX 128
+#define VER_WZRD_M_MIN 4
+#define VER_WZRD_M_MAX 432
+#define VER_WZRD_D_MIN 1
+#define VER_WZRD_D_MAX 123
+#define VER_WZRD_VCO_MIN 2160000000ULL
+#define VER_WZRD_VCO_MAX 4320000000ULL
+#define VER_WZRD_O_MIN 2
+#define VER_WZRD_O_MAX 511
#define WZRD_MIN_ERR 20000
#define WZRD_FRAC_POINTS 1000
@@ -135,6 +172,10 @@ struct clk_wzrd_divider {
spinlock_t *lock; /* divider lock */
};
+struct versal_clk_data {
+ bool is_versal;
+};
+
#define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
/* maximum frequencies for input/output clocks per speed grade */
@@ -147,6 +188,31 @@ static const unsigned long clk_wzrd_max_freq[] = {
/* spin lock variable for clk_wzrd */
static DEFINE_SPINLOCK(clkwzrd_lock);
+static unsigned long clk_wzrd_recalc_rate_ver(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ void __iomem *div_addr = divider->base + divider->offset;
+ u32 div, p5en, edge, prediv2, all;
+ unsigned int vall, valh;
+
+ edge = !!(readl(div_addr) & WZRD_CLKFBOUT_EDGE);
+ p5en = !!(readl(div_addr) & WZRD_P5EN);
+ prediv2 = !!(readl(div_addr) & WZRD_CLKOUT0_PREDIV2);
+ vall = readl(div_addr + 4) & WZRD_CLKFBOUT_L_MASK;
+ valh = readl(div_addr + 4) >> WZRD_CLKFBOUT_H_SHIFT;
+ all = valh + vall + edge;
+ if (!all)
+ all = 1;
+
+ if (prediv2)
+ div = 2 * all + prediv2 * p5en;
+ else
+ div = all;
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@@ -161,19 +227,64 @@ static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw,
divider->flags, divider->width);
}
+static int clk_wzrd_ver_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ void __iomem *div_addr = divider->base + divider->offset;
+ u32 value, regh, edged, p5en, p5fedge, regval, regval1;
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(divider->lock, flags);
+
+ value = DIV_ROUND_CLOSEST(parent_rate, rate);
+
+ regh = (value / 4);
+ regval1 = readl(div_addr);
+ regval1 |= WZRD_CLKFBOUT_PREDIV2;
+ regval1 = regval1 & ~(WZRD_CLKFBOUT_EDGE | WZRD_P5EN | WZRD_P5FEDGE);
+ if (value % 4 > 1) {
+ edged = 1;
+ regval1 |= (edged << WZRD_EDGE_SHIFT);
+ }
+ p5fedge = value % 2;
+ p5en = value % 2;
+ regval1 = regval1 | p5en << WZRD_P5EN_SHIFT | p5fedge << WZRD_P5FEDGE_SHIFT;
+ writel(regval1, div_addr);
+
+ regval = regh | regh << WZRD_CLKFBOUT_H_SHIFT;
+ writel(regval, div_addr + 4);
+ /* Check status register */
+ err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET,
+ value, value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+ if (err)
+ goto err_reconfig;
+
+ /* Initiate reconfiguration */
+ writel(WZRD_DR_BEGIN_DYNA_RECONF,
+ divider->base + WZRD_DR_INIT_VERSAL_OFFSET);
+
+ /* Check status register */
+ err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET,
+ value, value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+err_reconfig:
+ spin_unlock_irqrestore(divider->lock, flags);
+ return err;
+}
+
static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- int err;
- u32 value;
- unsigned long flags = 0;
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
void __iomem *div_addr = divider->base + divider->offset;
+ unsigned long flags;
+ u32 value;
+ int err;
- if (divider->lock)
- spin_lock_irqsave(divider->lock, flags);
- else
- __acquire(divider->lock);
+ spin_lock_irqsave(divider->lock, flags);
value = DIV_ROUND_CLOSEST(parent_rate, rate);
@@ -185,9 +296,9 @@ static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
writel(0x00, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);
/* Check status register */
- err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
- value, value & WZRD_DR_LOCK_BIT_MASK,
- WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+ err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET,
+ value, value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
if (err)
goto err_reconfig;
@@ -198,14 +309,11 @@ static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
divider->base + WZRD_DR_INIT_REG_OFFSET);
/* Check status register */
- err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
- value, value & WZRD_DR_LOCK_BIT_MASK,
- WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+ err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET,
+ value, value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
err_reconfig:
- if (divider->lock)
- spin_unlock_irqrestore(divider->lock, flags);
- else
- __release(divider->lock);
+ spin_unlock_irqrestore(divider->lock, flags);
return err;
}
@@ -223,18 +331,66 @@ static long clk_wzrd_round_rate(struct clk_hw *hw, unsigned long rate,
return *prate / div;
}
+static int clk_wzrd_get_divisors_ver(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ u64 vco_freq, freq, diff, vcomin, vcomax;
+ u32 m, d, o;
+ u32 mmin, mmax, dmin, dmax, omin, omax;
+
+ mmin = VER_WZRD_M_MIN;
+ mmax = VER_WZRD_M_MAX;
+ dmin = VER_WZRD_D_MIN;
+ dmax = VER_WZRD_D_MAX;
+ omin = VER_WZRD_O_MIN;
+ omax = VER_WZRD_O_MAX;
+ vcomin = VER_WZRD_VCO_MIN;
+ vcomax = VER_WZRD_VCO_MAX;
+
+ for (m = mmin; m <= mmax; m++) {
+ for (d = dmin; d <= dmax; d++) {
+ vco_freq = DIV_ROUND_CLOSEST((parent_rate * m), d);
+ if (vco_freq >= vcomin && vco_freq <= vcomax) {
+ for (o = omin; o <= omax; o++) {
+ freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o);
+ diff = abs(freq - rate);
+
+ if (diff < WZRD_MIN_ERR) {
+ divider->m = m;
+ divider->d = d;
+ divider->o = o;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return -EBUSY;
+}
+
static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
- unsigned long vco_freq, freq, diff;
+ u64 vco_freq, freq, diff, vcomin, vcomax;
u32 m, d, o;
-
- for (m = WZRD_M_MIN; m <= WZRD_M_MAX; m++) {
- for (d = WZRD_D_MIN; d <= WZRD_D_MAX; d++) {
+ u32 mmin, mmax, dmin, dmax, omin, omax;
+
+ mmin = WZRD_M_MIN;
+ mmax = WZRD_M_MAX;
+ dmin = WZRD_D_MIN;
+ dmax = WZRD_D_MAX;
+ omin = WZRD_O_MIN;
+ omax = WZRD_O_MAX;
+ vcomin = WZRD_VCO_MIN;
+ vcomax = WZRD_VCO_MAX;
+
+ for (m = mmin; m <= mmax; m++) {
+ for (d = dmin; d <= dmax; d++) {
vco_freq = DIV_ROUND_CLOSEST((parent_rate * m), d);
- if (vco_freq >= WZRD_VCO_MIN && vco_freq <= WZRD_VCO_MAX) {
- for (o = WZRD_O_MIN; o <= WZRD_O_MAX; o++) {
+ if (vco_freq >= vcomin && vco_freq <= vcomax) {
+ for (o = omin; o <= omax; o++) {
freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o);
diff = abs(freq - rate);
@@ -251,12 +407,99 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
return -EBUSY;
}
+static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div_addr)
+{
+ u32 value;
+ int err;
+
+ /* Check status register */
+ err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
+ value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+ if (err)
+ return -ETIMEDOUT;
+
+ /* Initiate reconfiguration */
+ writel(WZRD_DR_BEGIN_DYNA_RECONF, div_addr);
+ /* Check status register */
+ return readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
+ value & WZRD_DR_LOCK_BIT_MASK,
+ WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+}
+
+static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 regh, edged, p5en, p5fedge, value2, m, regval, regval1, value;
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ void __iomem *div_addr;
+ int err;
+
+ err = clk_wzrd_get_divisors_ver(hw, rate, parent_rate);
+ if (err)
+ return err;
+
+ writel(0, divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4));
+
+ m = divider->m;
+ edged = m % WZRD_DUTY_CYCLE;
+ regh = m / WZRD_DUTY_CYCLE;
+ regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKFBOUT_1));
+ regval1 |= WZRD_MULT_PREDIV2;
+ if (edged)
+ regval1 = regval1 | WZRD_CLKFBOUT_EDGE;
+ else
+ regval1 = regval1 & ~WZRD_CLKFBOUT_EDGE;
+
+ writel(regval1, divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKFBOUT_1));
+ regval1 = regh | regh << WZRD_CLKFBOUT_H_SHIFT;
+ writel(regval1, divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKFBOUT_2));
+
+ value2 = divider->d;
+ edged = value2 % WZRD_DUTY_CYCLE;
+ regh = (value2 / WZRD_DUTY_CYCLE);
+ regval1 = FIELD_PREP(WZRD_DIVCLK_EDGE, edged);
+ writel(regval1, divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_DESKEW_2));
+ regval1 = regh | regh << WZRD_CLKFBOUT_H_SHIFT;
+ writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK));
+
+ value = divider->o;
+ regh = value / WZRD_O_DIV;
+ regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKOUT0_1));
+ regval1 |= WZRD_CLKFBOUT_PREDIV2;
+ regval1 = regval1 & ~(WZRD_CLKFBOUT_EDGE | WZRD_P5EN | WZRD_P5FEDGE);
+
+ if (value % WZRD_O_DIV > 1) {
+ edged = 1;
+ regval1 |= edged << WZRD_CLKFBOUT_H_SHIFT;
+ }
+
+ p5fedge = value % WZRD_DUTY_CYCLE;
+ p5en = value % WZRD_DUTY_CYCLE;
+
+ regval1 = regval1 | FIELD_PREP(WZRD_P5EN, p5en) | FIELD_PREP(WZRD_P5FEDGE, p5fedge);
+ writel(regval1, divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKOUT0_1));
+ regval = regh | regh << WZRD_CLKFBOUT_H_SHIFT;
+ writel(regval, divider->base + WZRD_CLK_CFG_REG(1,
+ WZRD_CLKOUT0_2));
+ div_addr = divider->base + WZRD_DR_INIT_VERSAL_OFFSET;
+
+ return clk_wzrd_reconfig(divider, div_addr);
+}
+
static int clk_wzrd_dynamic_all_nolock(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
unsigned long vco_freq, rate_div, clockout0_div;
- u32 reg, pre, value, f;
+ void __iomem *div_addr = divider->base;
+ u32 reg, pre, f;
int err;
err = clk_wzrd_get_divisors(hw, rate, parent_rate);
@@ -275,35 +518,22 @@ static int clk_wzrd_dynamic_all_nolock(struct clk_hw *hw, unsigned long rate,
reg = FIELD_PREP(WZRD_CLKOUT_DIVIDE_MASK, clockout0_div) |
FIELD_PREP(WZRD_CLKOUT0_FRAC_MASK, f);
- writel(reg, divider->base + WZRD_CLK_CFG_REG(2));
+ writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 2));
/* Set divisor and clear phase offset */
reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) |
FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d);
- writel(reg, divider->base + WZRD_CLK_CFG_REG(0));
- writel(divider->o, divider->base + WZRD_CLK_CFG_REG(2));
- writel(0, divider->base + WZRD_CLK_CFG_REG(3));
- /* Check status register */
- err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
- value & WZRD_DR_LOCK_BIT_MASK,
- WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
- if (err)
- return -ETIMEDOUT;
-
- /* Initiate reconfiguration */
- writel(WZRD_DR_BEGIN_DYNA_RECONF,
- divider->base + WZRD_DR_INIT_REG_OFFSET);
-
- /* Check status register */
- return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
- value & WZRD_DR_LOCK_BIT_MASK,
- WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
+ writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 0));
+ writel(divider->o, divider->base + WZRD_CLK_CFG_REG(0, 2));
+ writel(0, divider->base + WZRD_CLK_CFG_REG(0, 3));
+ div_addr = divider->base + WZRD_DR_INIT_REG_OFFSET;
+ return clk_wzrd_reconfig(divider, div_addr);
}
static int clk_wzrd_dynamic_all(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
- unsigned long flags = 0;
+ unsigned long flags;
int ret;
spin_lock_irqsave(divider->lock, flags);
@@ -315,21 +545,103 @@ static int clk_wzrd_dynamic_all(struct clk_hw *hw, unsigned long rate,
return ret;
}
+static int clk_wzrd_dynamic_all_ver(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(divider->lock, flags);
+
+ ret = clk_wzrd_dynamic_ver_all_nolock(hw, rate, parent_rate);
+
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return ret;
+}
+
static unsigned long clk_wzrd_recalc_rate_all(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
u32 m, d, o, div, reg, f;
- reg = readl(divider->base + WZRD_CLK_CFG_REG(0));
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 0));
d = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
m = FIELD_GET(WZRD_CLKFBOUT_MULT_MASK, reg);
- reg = readl(divider->base + WZRD_CLK_CFG_REG(2));
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 2));
o = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
f = FIELD_GET(WZRD_CLKOUT0_FRAC_MASK, reg);
div = DIV_ROUND_CLOSEST(d * (WZRD_FRAC_POINTS * o + f), WZRD_FRAC_POINTS);
return divider_recalc_rate(hw, parent_rate * m, div, divider->table,
+ divider->flags, divider->width);
+}
+
+static unsigned long clk_wzrd_recalc_rate_all_ver(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
+ u32 edged, div2, p5en, edge, prediv2, all, regl, regh, mult;
+ u32 div, reg;
+
+ edge = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_1)) &
+ WZRD_CLKFBOUT_EDGE);
+
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_2));
+ regl = FIELD_GET(WZRD_CLKFBOUT_L_MASK, reg);
+ regh = FIELD_GET(WZRD_CLKFBOUT_H_MASK, reg);
+
+ mult = regl + regh + edge;
+ if (!mult)
+ mult = 1;
+
+ regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4)) &
+ WZRD_CLKFBOUT_FRAC_EN;
+ if (regl) {
+ regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_3))
+ & WZRD_VERSAL_FRAC_MASK;
+ mult = mult * WZRD_FRAC_GRADIENT + regl;
+ parent_rate = DIV_ROUND_CLOSEST((parent_rate * mult), WZRD_FRAC_GRADIENT);
+ } else {
+ parent_rate = parent_rate * mult;
+ }
+
+ /* O Calculation */
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_1));
+ edged = FIELD_GET(WZRD_CLKFBOUT_EDGE, reg);
+ p5en = FIELD_GET(WZRD_P5EN, reg);
+ prediv2 = FIELD_GET(WZRD_CLKOUT0_PREDIV2, reg);
+
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_2));
+ /* Low time */
+ regl = FIELD_GET(WZRD_CLKFBOUT_L_MASK, reg);
+ /* High time */
+ regh = FIELD_GET(WZRD_CLKFBOUT_H_MASK, reg);
+ all = regh + regl + edged;
+ if (!all)
+ all = 1;
+
+ if (prediv2)
+ div2 = PREDIV2_MULT * all + p5en;
+ else
+ div2 = all;
+
+ /* D calculation */
+ edged = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DESKEW_2)) &
+ WZRD_DIVCLK_EDGE);
+ reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK));
+ /* Low time */
+ regl = FIELD_GET(WZRD_CLKFBOUT_L_MASK, reg);
+ /* High time */
+ regh = FIELD_GET(WZRD_CLKFBOUT_H_MASK, reg);
+ div = regl + regh + edged;
+ if (!div)
+ div = 1;
+
+ div = div * div2;
+ return divider_recalc_rate(hw, parent_rate, div, divider->table,
divider->flags, divider->width);
}
@@ -360,6 +672,18 @@ static long clk_wzrd_round_rate_all(struct clk_hw *hw, unsigned long rate,
return rate;
}
+static const struct clk_ops clk_wzrd_ver_divider_ops = {
+ .round_rate = clk_wzrd_round_rate,
+ .set_rate = clk_wzrd_ver_dynamic_reconfig,
+ .recalc_rate = clk_wzrd_recalc_rate_ver,
+};
+
+static const struct clk_ops clk_wzrd_ver_div_all_ops = {
+ .round_rate = clk_wzrd_round_rate_all,
+ .set_rate = clk_wzrd_dynamic_all_ver,
+ .recalc_rate = clk_wzrd_recalc_rate_all_ver,
+};
+
static const struct clk_ops clk_wzrd_clk_divider_ops = {
.round_rate = clk_wzrd_round_rate,
.set_rate = clk_wzrd_dynamic_reconfig,
@@ -484,6 +808,53 @@ static struct clk *clk_wzrd_register_divf(struct device *dev,
return hw->clk;
}
+static struct clk *clk_wzrd_ver_register_divider(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *base,
+ u16 offset,
+ u8 shift, u8 width,
+ u8 clk_divider_flags,
+ u32 div_type,
+ spinlock_t *lock)
+{
+ struct clk_wzrd_divider *div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
+ init.ops = &clk_divider_ro_ops;
+ else if (div_type == DIV_O)
+ init.ops = &clk_wzrd_ver_divider_ops;
+ else
+ init.ops = &clk_wzrd_ver_div_all_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ div->base = base;
+ div->offset = offset;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->lock = lock;
+ div->hw.init = &init;
+
+ hw = &div->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return hw->clk;
+}
+
static struct clk *clk_wzrd_register_divider(struct device *dev,
const char *name,
const char *parent_name,
@@ -588,18 +959,24 @@ static int __maybe_unused clk_wzrd_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
clk_wzrd_resume);
+static const struct versal_clk_data versal_data = {
+ .is_versal = true,
+};
+
static int clk_wzrd_probe(struct platform_device *pdev)
{
- int i, ret;
+ const char *clkout_name, *clk_name, *clk_mul_name;
+ u32 regl, regh, edge, regld, reghd, edged, div;
+ struct device_node *np = pdev->dev.of_node;
+ const struct versal_clk_data *data;
+ struct clk_wzrd *clk_wzrd;
+ unsigned long flags = 0;
+ void __iomem *ctrl_reg;
u32 reg, reg_f, mult;
+ bool is_versal = false;
unsigned long rate;
- const char *clk_name;
- void __iomem *ctrl_reg;
- struct clk_wzrd *clk_wzrd;
- const char *clkout_name;
- struct device_node *np = pdev->dev.of_node;
int nr_outputs;
- unsigned long flags = 0;
+ int i, ret;
clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
if (!clk_wzrd)
@@ -641,6 +1018,10 @@ static int clk_wzrd_probe(struct platform_device *pdev)
goto err_disable_clk;
}
+ data = device_get_match_data(&pdev->dev);
+ if (data)
+ is_versal = data->is_versal;
+
ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs);
if (ret || nr_outputs > WZRD_NUM_OUTPUTS) {
ret = -EINVAL;
@@ -653,26 +1034,61 @@ static int clk_wzrd_probe(struct platform_device *pdev)
goto err_disable_clk;
}
- if (nr_outputs == 1) {
- clk_wzrd->clkout[0] = clk_wzrd_register_divider
+ if (is_versal) {
+ if (nr_outputs == 1) {
+ clk_wzrd->clkout[0] = clk_wzrd_ver_register_divider
(&pdev->dev, clkout_name,
__clk_get_name(clk_wzrd->clk_in1), 0,
- clk_wzrd->base, WZRD_CLK_CFG_REG(3),
+ clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3),
WZRD_CLKOUT_DIVIDE_SHIFT,
WZRD_CLKOUT_DIVIDE_WIDTH,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
DIV_ALL, &clkwzrd_lock);
- goto out;
- }
-
- reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0));
- reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
- reg_f = reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;
+ goto out;
+ }
+ /* register multiplier */
+ edge = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0)) &
+ BIT(8));
+ regl = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) &
+ WZRD_CLKFBOUT_L_MASK) >> WZRD_CLKFBOUT_L_SHIFT;
+ regh = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) &
+ WZRD_CLKFBOUT_H_MASK) >> WZRD_CLKFBOUT_H_SHIFT;
+ mult = regl + regh + edge;
+ if (!mult)
+ mult = 1;
+ mult = mult * WZRD_FRAC_GRADIENT;
+
+ regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 51)) &
+ WZRD_CLKFBOUT_FRAC_EN;
+ if (regl) {
+ regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 48)) &
+ WZRD_VERSAL_FRAC_MASK;
+ mult = mult + regl;
+ }
+ div = 64;
+ } else {
+ if (nr_outputs == 1) {
+ clk_wzrd->clkout[0] = clk_wzrd_register_divider
+ (&pdev->dev, clkout_name,
+ __clk_get_name(clk_wzrd->clk_in1), 0,
+ clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3),
+ WZRD_CLKOUT_DIVIDE_SHIFT,
+ WZRD_CLKOUT_DIVIDE_WIDTH,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+ DIV_ALL, &clkwzrd_lock);
- reg = reg & WZRD_CLKFBOUT_MULT_MASK;
- reg = reg >> WZRD_CLKFBOUT_MULT_SHIFT;
- mult = (reg * 1000) + reg_f;
+ goto out;
+ }
+ reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0));
+ reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
+ reg_f = reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;
+
+ reg = reg & WZRD_CLKFBOUT_MULT_MASK;
+ reg = reg >> WZRD_CLKFBOUT_MULT_SHIFT;
+ mult = (reg * 1000) + reg_f;
+ div = 1000;
+ }
clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
if (!clk_name) {
ret = -ENOMEM;
@@ -681,7 +1097,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
(&pdev->dev, clk_name,
__clk_get_name(clk_wzrd->clk_in1),
- 0, mult, 1000);
+ 0, mult, div);
if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
@@ -694,13 +1110,29 @@ static int clk_wzrd_probe(struct platform_device *pdev)
goto err_rm_int_clk;
}
- ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(0);
- /* register div */
- clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider
+ if (is_versal) {
+ edged = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 20)) &
+ BIT(10));
+ regld = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) &
+ WZRD_CLKFBOUT_L_MASK) >> WZRD_CLKFBOUT_L_SHIFT;
+ reghd = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) &
+ WZRD_CLKFBOUT_H_MASK) >> WZRD_CLKFBOUT_H_SHIFT;
+ div = (regld + reghd + edged);
+ if (!div)
+ div = 1;
+
+ clk_mul_name = __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]);
+ clk_wzrd->clks_internal[wzrd_clk_mul_div] =
+ clk_register_fixed_factor(&pdev->dev, clk_name,
+ clk_mul_name, 0, 1, div);
+ } else {
+ ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0);
+ clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider
(&pdev->dev, clk_name,
__clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
flags, ctrl_reg, 0, 8, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &clkwzrd_lock);
+ }
if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
dev_err(&pdev->dev, "unable to register divider clock\n");
ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
@@ -716,24 +1148,35 @@ static int clk_wzrd_probe(struct platform_device *pdev)
goto err_rm_int_clk;
}
- if (!i)
- clk_wzrd->clkout[i] = clk_wzrd_register_divf
- (&pdev->dev, clkout_name,
- clk_name, flags,
- clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
- WZRD_CLKOUT_DIVIDE_SHIFT,
- WZRD_CLKOUT_DIVIDE_WIDTH,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
- DIV_O, &clkwzrd_lock);
- else
- clk_wzrd->clkout[i] = clk_wzrd_register_divider
- (&pdev->dev, clkout_name,
- clk_name, 0,
- clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
- WZRD_CLKOUT_DIVIDE_SHIFT,
- WZRD_CLKOUT_DIVIDE_WIDTH,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
- DIV_O, &clkwzrd_lock);
+ if (is_versal) {
+ clk_wzrd->clkout[i] = clk_wzrd_ver_register_divider
+ (&pdev->dev,
+ clkout_name, clk_name, 0,
+ clk_wzrd->base,
+ (WZRD_CLK_CFG_REG(is_versal, 3) + i * 8),
+ WZRD_CLKOUT_DIVIDE_SHIFT,
+ WZRD_CLKOUT_DIVIDE_WIDTH,
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ALLOW_ZERO,
+ DIV_O, &clkwzrd_lock);
+ } else {
+ if (!i)
+ clk_wzrd->clkout[i] = clk_wzrd_register_divf
+ (&pdev->dev, clkout_name, clk_name, flags, clk_wzrd->base,
+ (WZRD_CLK_CFG_REG(is_versal, 2) + i * 12),
+ WZRD_CLKOUT_DIVIDE_SHIFT,
+ WZRD_CLKOUT_DIVIDE_WIDTH,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+ DIV_O, &clkwzrd_lock);
+ else
+ clk_wzrd->clkout[i] = clk_wzrd_register_divider
+ (&pdev->dev, clkout_name, clk_name, 0, clk_wzrd->base,
+ (WZRD_CLK_CFG_REG(is_versal, 2) + i * 12),
+ WZRD_CLKOUT_DIVIDE_SHIFT,
+ WZRD_CLKOUT_DIVIDE_WIDTH,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+ DIV_O, &clkwzrd_lock);
+ }
if (IS_ERR(clk_wzrd->clkout[i])) {
int j;
@@ -799,9 +1242,10 @@ static void clk_wzrd_remove(struct platform_device *pdev)
}
static const struct of_device_id clk_wzrd_ids[] = {
- { .compatible = "xlnx,clocking-wizard" },
- { .compatible = "xlnx,clocking-wizard-v5.2" },
- { .compatible = "xlnx,clocking-wizard-v6.0" },
+ { .compatible = "xlnx,versal-clk-wizard", .data = &versal_data },
+ { .compatible = "xlnx,clocking-wizard" },
+ { .compatible = "xlnx,clocking-wizard-v5.2" },
+ { .compatible = "xlnx,clocking-wizard-v6.0" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
index 60359333f26d..9b5d3050b742 100644
--- a/drivers/clk/zynqmp/clk-mux-zynqmp.c
+++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -89,7 +89,7 @@ static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
static const struct clk_ops zynqmp_clk_mux_ops = {
.get_parent = zynqmp_clk_mux_get_parent,
.set_parent = zynqmp_clk_mux_set_parent,
- .determine_rate = __clk_mux_determine_rate,
+ .determine_rate = __clk_mux_determine_rate_closest,
};
static const struct clk_ops zynqmp_clk_mux_ro_ops = {
diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c
index 33a3b2a22659..5a00487ae408 100644
--- a/drivers/clk/zynqmp/divider.c
+++ b/drivers/clk/zynqmp/divider.c
@@ -110,52 +110,6 @@ static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_UP_ULL(parent_rate, value);
}
-static void zynqmp_get_divider2_val(struct clk_hw *hw,
- unsigned long rate,
- struct zynqmp_clk_divider *divider,
- u32 *bestdiv)
-{
- int div1;
- int div2;
- long error = LONG_MAX;
- unsigned long div1_prate;
- struct clk_hw *div1_parent_hw;
- struct zynqmp_clk_divider *pdivider;
- struct clk_hw *div2_parent_hw = clk_hw_get_parent(hw);
-
- if (!div2_parent_hw)
- return;
-
- pdivider = to_zynqmp_clk_divider(div2_parent_hw);
- if (!pdivider)
- return;
-
- div1_parent_hw = clk_hw_get_parent(div2_parent_hw);
- if (!div1_parent_hw)
- return;
-
- div1_prate = clk_hw_get_rate(div1_parent_hw);
- *bestdiv = 1;
- for (div1 = 1; div1 <= pdivider->max_div;) {
- for (div2 = 1; div2 <= divider->max_div;) {
- long new_error = ((div1_prate / div1) / div2) - rate;
-
- if (abs(new_error) < abs(error)) {
- *bestdiv = div2;
- error = new_error;
- }
- if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
- div2 = div2 << 1;
- else
- div2++;
- }
- if (pdivider->flags & CLK_DIVIDER_POWER_OF_TWO)
- div1 = div1 << 1;
- else
- div1++;
- }
-}
-
/**
* zynqmp_clk_divider_round_rate() - Round rate of divider clock
* @hw: handle between common and hardware-specific interfaces
@@ -174,6 +128,7 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
u32 div_type = divider->div_type;
u32 bestdiv;
int ret;
+ u8 width;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
@@ -193,23 +148,12 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
}
- bestdiv = zynqmp_divider_get_val(*prate, rate, divider->flags);
-
- /*
- * In case of two divisors, compute best divider values and return
- * divider2 value based on compute value. div1 will be automatically
- * set to optimum based on required total divider value.
- */
- if (div_type == TYPE_DIV2 &&
- (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
- zynqmp_get_divider2_val(hw, rate, divider, &bestdiv);
- }
+ width = fls(divider->max_div);
- if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac)
- bestdiv = rate % *prate ? 1 : bestdiv;
+ rate = divider_round_rate(hw, rate, prate, NULL, width, divider->flags);
- bestdiv = min_t(u32, bestdiv, divider->max_div);
- *prate = rate * bestdiv;
+ if (divider->is_frac && (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && (rate % *prate))
+ *prate = rate;
return rate;
}