1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2019 DENX Software Engineering
4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
6 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
7 * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
8 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
15 #include <clk-uclass.h>
16 #include <dm/device.h>
17 #include <dm/uclass.h>
19 #include <dm/device-internal.h>
20 #include <linux/clk-provider.h>
25 #define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
27 static unsigned int _get_table_div(const struct clk_div_table *table,
30 const struct clk_div_table *clkt;
32 for (clkt = table; clkt->div; clkt++)
38 static unsigned int _get_div(const struct clk_div_table *table,
39 unsigned int val, unsigned long flags, u8 width)
41 if (flags & CLK_DIVIDER_ONE_BASED)
43 if (flags & CLK_DIVIDER_POWER_OF_TWO)
45 if (flags & CLK_DIVIDER_MAX_AT_ZERO)
46 return val ? val : clk_div_mask(width) + 1;
48 return _get_table_div(table, val);
52 unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
54 const struct clk_div_table *table,
55 unsigned long flags, unsigned long width)
59 div = _get_div(table, val, flags, width);
61 WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
62 "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
67 return DIV_ROUND_UP_ULL((u64)parent_rate, div);
70 static ulong clk_divider_recalc_rate(struct clk *clk)
72 struct clk_divider *divider =
73 to_clk_divider(dev_get_clk_ptr(clk->dev));
74 unsigned long parent_rate = clk_get_parent_rate(clk);
77 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
78 val = divider->io_divider_val;
80 val = readl(divider->reg);
82 val >>= divider->shift;
83 val &= clk_div_mask(divider->width);
85 return divider_recalc_rate(clk, parent_rate, val, divider->table,
86 divider->flags, divider->width);
89 const struct clk_ops clk_divider_ops = {
90 .get_rate = clk_divider_recalc_rate,
93 static struct clk *_register_divider(struct device *dev, const char *name,
94 const char *parent_name, unsigned long flags,
95 void __iomem *reg, u8 shift, u8 width,
96 u8 clk_divider_flags, const struct clk_div_table *table)
98 struct clk_divider *div;
102 if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
103 if (width + shift > 16) {
104 pr_warn("divider value exceeds LOWORD field\n");
105 return ERR_PTR(-EINVAL);
109 /* allocate the divider */
110 div = kzalloc(sizeof(*div), GFP_KERNEL);
112 return ERR_PTR(-ENOMEM);
114 /* struct clk_divider assignments */
118 div->flags = clk_divider_flags;
120 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
121 div->io_divider_val = *(u32 *)reg;
124 /* register the clock */
127 ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
136 struct clk *clk_register_divider(struct device *dev, const char *name,
137 const char *parent_name, unsigned long flags,
138 void __iomem *reg, u8 shift, u8 width,
139 u8 clk_divider_flags)
143 clk = _register_divider(dev, name, parent_name, flags, reg, shift,
144 width, clk_divider_flags, NULL);
146 return ERR_CAST(clk);
150 U_BOOT_DRIVER(ccf_clk_divider) = {
151 .name = UBOOT_DM_CLK_CCF_DIVIDER,
153 .ops = &clk_divider_ops,
154 .flags = DM_FLAG_PRE_RELOC,